资讯专栏INFORMATION COLUMN

JavaScript同步、异步及事件循环

zr_hebo / 1585人阅读

摘要:同步异步是单线程的,每次只能做一件事情。像以下这种情况,代码会按顺序执行,这个就叫同步。虽然是单线程,但是浏览器是多线程的,在遇到像事件等这种任务时,会转交给浏览器的其他工作线程上面提到的几个线程执行,执行完之后将回调函数放入到任务队列。

同步、异步

JS是单线程的,每次只能做一件事情。像以下这种情况,代码会按顺序执行,这个就叫同步。

console.log(1);
console.log(2);
console.log(3);

以下代码会输出2、3、1,像这种不按顺序执行的,或者说代码执行中间有时间间隙的,叫异步。

setTimeout(() => {
    console.log(1);
}, 0);
console.log(2);
console.log(3);
事件循环

一个浏览器通常有以下几个常驻的线程:

渲染引擎线程:该线程负责页面的渲染

JS引擎线程:负责JS的解析和执行

定时触发器线程:处理定时事件,比如setTimeout, setInterval

事件触发线程:处理DOM事件

异步http请求线程:处理http请求

渲染线程和JS引擎线程是不能同时进行的。也就是说在执行代码时,渲染会挂起;渲染DOM时,代码也不会执行。
虽然JS是单线程,但是浏览器是多线程的,在遇到像setTimeout、DOM事件、ajax等这种任务时,会转交给浏览器的其他工作线程(上面提到的几个线程)执行,执行完之后将回调函数放入到任务队列。

在JS运行环境里,除了主线程外,还有任务队列。

// eventLoop是一个用作队列的数组
// (先进,先出)
var eventLoop = [ ];
var event;
// “永远”执行
while (true) {
    // 一次tick
    if (eventLoop.length > 0) {
        // 拿到队列中的下一个事件
        event = eventLoop.shift();
        // 现在,执行下一个事件
        event();
    }
}

我们可以用上面的代码来想像一下JS的执行情况。

JS主线程,就像是一个while循环,会一直执行下去。在这期间,每次都会查看任务队列有没有需要执行的任务(回调函数)。在执行完一个任务之后,会继续下一个循环,直到任务队列所有任务都执行完为止。

microtask(微任务)、macrotask(宏任务)

任务队列又分微任务队列和宏任务队列

微任务

Promise

MutationObserver

Object.observe()(已废弃)

宏任务

setTimeout

setInterval

setImmediate

IO

UI rendering(DOM event)

执行过程

在JS执行完同步任务之后,会开始执行微任务队列

在将所有的微任务执行完之后,会开始执行宏任务队列

在执行完一个宏任务之后,跳出来,重新开始下一个循环(从1开始执行)

也就是说执行微任务队列 会将队列中的所有微任务执行完 而执行宏任务队列 每次只执行一个宏任务 然后重新开始下一个循环
我们可以看看以下代码

setTimeout(() => {
    console.log(3)
    new Promise((resolve, reject) => {
        console.log(5)
        resolve()
    }).then(console.log(6))
}, 0)

setTimeout(() => {
    console.log(4)
}, 0)

new Promise((resolve, reject) => {
    console.log(1)
    resolve()
}).then(console.log(2))

输出是1 2 3 5 6 4

我们来分析一下代码的执行过程

前面的两个setTimeout都是宏任务,所以现在宏任务队列有2个任务

Promise里面的代码是同步任务,所以现在会马上执行 输出1

Promise的then是微任务,所以现在微任务队列有1个任务

在执行完同步任务之后,开始执行微任务,也就是console.log(2), 输出2

在执行完微任务之后,会执行宏任务,第一个宏任务也就是第一个setTimeout

第一个setTimeout会先输出3,然后输出5,因为这两个都是同步任务,然后遇到then,加入微任务队列,宏任务执行完重新开始下一个循环。

因为没有同步代码,所以接着执行微任务,此时微任务队列有1个任务(第6步加入), 宏任务队列还有1个任务(第6步执行完了第一个宏任务)

执行微任务,输出6

再执行宏任务,输出4

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100395.html

相关文章

  • 浅析JavaScript异步

    摘要:回调函数,一般在同步情境下是最后执行的,而在异步情境下有可能不执行,因为事件没有被触发或者条件不满足。同步方式请求异步同步请求当请求开始发送时,浏览器事件线程通知主线程,让线程发送数据请求,主线程收到 一直以来都知道JavaScript是一门单线程语言,在笔试过程中不断的遇到一些输出结果的问题,考量的是对异步编程掌握情况。一般被问到异步的时候脑子里第一反应就是Ajax,setTimse...

    Tangpj 评论0 收藏0
  • JavaScript 工作原理之四-事件循环异步编程的出现和 5 种更好的 async/await

    摘要:函数会在之后的某个时刻触发事件定时器。事件循环中的这样一次遍历被称为一个。执行完毕并出栈。当定时器过期,宿主环境会把回调函数添加至事件循环队列中,然后,在未来的某个取出并执行该事件。 原文请查阅这里,略有改动。 本系列持续更新中,Github 地址请查阅这里。 这是 JavaScript 工作原理的第四章。 现在,我们将会通过回顾单线程环境下编程的弊端及如何克服这些困难以创建令人惊叹...

    maochunguang 评论0 收藏0
  • (转)JavaScript同步异步事件循环

    摘要:事件循环事件循环是指主线程重复从消息队列中取消息执行的过程。事件触发时,表示异步任务完成,会将事件监听器函数封装成一条消息放到消息队列中,等待主线程执行。 一. 单线程 我们常说JavaScript是单线程的。 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。不妨叫它主线程。 但是实际上还存在其他的线程。例如:处理AJAX请求的线程、处理DOM事件的线...

    android_c 评论0 收藏0
  • js的单线程,异步回调函数

    摘要:当主线程开始执行异步任务,实际就是执行对应的回调函数。异步任务必须指定回调函数。所以注意的是,只是将事件插入了任务队列,必须等到当前代码执行栈执行完,主线程才会去执行它指定的回调函数。 最近本人对于js的运行机制,特别是异步,还有回调函数感觉很乱,于是参考了很多有用的博客(博客原文地址会在文末给出),整理如下: js单线程 我们都知道,Javascript语言的执行环境是单线程(si...

    Songlcy 评论0 收藏0

发表评论

0条评论

zr_hebo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<