资讯专栏INFORMATION COLUMN

js事件循环机制(浏览器端Event Loop) 以及async/await的理解

CKJOKER / 1634人阅读

摘要:接下来处理微任务队列,打印,后面一个不会有任何打印,但是会执行执行后面的代码打印进入第二次事件循环,执行宏任务队列打印

事件循环机制

理解js的事件循环机制,能够很大程度的帮我们更深层次的理解平时遇到的一些很疑惑的问题

简单版本

下面来看一段代码,想想它的结果和你的结果是否一样

    setTimeout(function() {
        console.log(1)
    }, 0)
    
    console.log(2)
    
    //  执行结果是 2 1

我们可以将js的任务分为同步任务异步任务, 按照这种分类js的执行机制如下

任务执行队列分为同步任务队列异步任务队列

代码执行时,遇到同步代码,会被直接推入同步任务队列并依次执行

遇到异步代码(如setTimeout、setInterval), 会被直接推入异步任务队列

同步任务队列执行完毕,这个时候异步任务队列的任务会被依次推入同步任务队列并依次执行

所以上面的代码执行的时候, setTimeout()不会被立即执行,会被推到异步任务队列里面, 之后再执行console.log(2), 同步任务队列任务执行完毕之后,会去异步任务队列的任务会被依次推到 同步任务队列并执行

终极版本

下面来看一段代码,想想它的结果和你的结果是否一样

    setTimeout(function() {
        console.log(1)
    }, 0)
    
    new Promise(function(resolve, reject) {
        console.log(2)
        resolve()
    }).then((res) => {
        console.log(3)
    })
    console.log(4)
    
    // 执行结果是 2 4 3 1

js异步任务按照准确的划分,应该将任务分为

宏任务: setTimeoutsetInterval

微任务: 例如Promise.then方法。注意new Promsie()的时候是同步,立即执行。

注意: 现在有三个队列: 同步队列(也称执行栈)、宏任务队列、微任务队列

所以针对这种机制,js的事件循环机制应该是这样的

遇到同步代码,依次推入同步队列并执行

当遇到setTimeout、setInterval,会被推到宏任务队列

如果遇到.then,会被当作微任务,被推入微任务队列

同步队列执行完毕,然后会去微队列取任务,直到微队列清空。然后检查宏队列,去宏队列取任务,并且每一个宏任务执行完毕都会去微队列跑一遍,看看有没有新的微任务,有的话再把微任务清空。这样依次循环

 console.log(1);
    
 setTimeout(() => {
   console.log("setTimeout");
 }, 0);

 let promise = new Promise(resolve => {
   console.log(3);
   resolve();
 }).then(data => {
   console.log(100);
 }).then(data => {
   console.log(200);
 });
    
 console.log(2);

所以对于以上的代码执行流程如下:

遇到同步任务先输出1。

setTimeout是宏任务,会先放到宏任务队列中。

new Promise是立即执行的,所以会先输出3。

Promise.then是微任务,会依次排列到微任务队列中,继续向下执行输出2。

现在执行栈中的任务已经清空,再将微任务队列清空,依次输出100和200。

然后每次取出一个宏任务,因为现在只有一个宏任务,所以最后输出setTimeout

async/await async

当我们在函数前使用async的时候,使得该函数返回的是一个Promise对象

async function test() {
    return 1   // async的函数会在这里帮我们隐士使用Promise.resolve(1)
}
// 等价于下面的代码
function test() {
   return new Promise(function(resolve, reject) {
       resolve(1)
   })
}

可见async只是一个语法糖,只是帮助我们返回一个Promise而已

await

await表示等待,是右侧「表达式」的结果,这个表达式的计算结果可以是 Promise 对象的值或者一个函数的值(换句话说,就是没有特殊限定)。并且只能在带有async的内部使用

使用await时,会从右往左执行,当遇到await时,会阻塞函数内部处于它后面的代码,去执行该函数外部的同步代码,当外部同步代码执行完毕,再回到该函数内部执行剩余的代码, 并且当await执行完毕之后,会先处理微任务队列的代码

下面来看一个栗子:

        async function async1() {
            console.log( "async1 start" )
            await async2()
            console.log( "async1 end" )
        }
        async function async2() {
            console.log( "async2" )
        }
        console.log( "script start" )
        setTimeout( function () {
            console.log( "setTimeout" )
        }, 0 )
        async1();
        new Promise( function ( resolve ) {
            console.log( "promise1" )
            resolve();
        } ).then( function () {
            console.log( "promise2" )
        } )
        console.log( "script end" )
        
       

下面是在chrome浏览器上输出的结果

使用事件循环机制分析:

首先执行同步代码,console.log( "script start" )

遇到setTimeout,会被推入宏任务队列

执行async1(), 它也是同步的,只是返回值是Promise,在内部首先执行console.log( "async1 start" )

然后执行async2(), 然后会打印console.log( "async2" )

从右到左会执行, 当遇到await的时候,阻塞后面的代码,去外部执行同步代码

进入 new Promise,打印console.log( "promise1" )

.then放入事件循环的微任务队列

继续执行,打印console.log( "script end" )

外部同步代码执行完毕,接着回到async1()内部, 由于async2()其实是返回一个Promise, await async2()相当于获取它的值,其实就相当于这段代码Promise.resolve(undefined).then((undefined) => {}),所以.then会被推入微任务队列, 所以现在微任务队列会有两个任务。接下来处理微任务队列,打印console.log( "promise2" ),后面一个.then不会有任何打印,但是会执行

执行后面的代码, 打印console.log( "async1 end" )

进入第二次事件循环,执行宏任务队列, 打印console.log( "setTimeout" )

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

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

相关文章

  • 最后一次搞懂 Event Loop

    摘要:由于是单线程的,这些方法就会按顺序被排列在一个单独的地方,这个地方就是所谓执行栈。事件队列每次仅执行一个任务,在该任务执行完毕之后,再执行下一个任务。 Event Loop 是 JavaScript 异步编程的核心思想,也是前端进阶必须跨越的一关。同时,它又是面试的必考点,特别是在 Promise 出现之后,各种各样的面试题层出不穷,花样百出。这篇文章从现实生活中的例子入手,让你彻底理解 E...

    gself 评论0 收藏0
  • 笔试题之Event Loop终极篇

    摘要:下面开始分析开头的代码第一轮事件循环流程整体作为第一个宏任务进入主线程,遇到,输出遇到函数声明,声明暂时不用管遇到,其回调函数被分发到微任务中。我们记为遇到,其回调函数被分发到宏任务中。 先上一道常见的笔试题 console.log(1); async function async1() { console.log(2); await async2(); con...

    niceforbear 评论0 收藏0
  • 由setTimeout深入JavaScript执行环境异步机制

    摘要:图片转引自的演讲和两个定时器中回调的执行逻辑便是典型的机制。异步编程关于异步编程我的理解是,在执行环境所提供的异步机制之上,在应用编码层面上实现整体流程控制的异步风格。 问题背景 在一次开发任务中,需要实现如下一个饼状图动画,基于canvas进行绘图,但由于对于JS运行环境中异步机制的不了解,所以遇到了一个棘手的问题,始终无法解决,之后在与同事交流之后才恍然大悟。问题的根节在于经典的J...

    codeGoogle 评论0 收藏0
  • 面试题之Event Loop终极篇

    摘要:下面开始分析开头的代码第一轮事件循环流程整体作为第一个宏任务进入主线程,遇到,输出遇到函数声明,声明暂时不用管遇到,其回调函数被分发到微任务中。我们记为遇到,其回调函数被分发到宏任务中。 先上一道常见的笔试题 console.log(1); async function async1() { console.log(2); await async2(); con...

    233jl 评论0 收藏0
  • 理清览器事件循环机制Event Loop

    摘要:何为事件循环机制的任务分两种,分别是同步任务和异步任务。如上图所示主线程在执行代码的时候,遇到异步任务进入并注册回调函数,有了运行结果后将它添加到事件队列中,然后继续执行下面的代码,直到同步代码执行完。 我们知道,JavaScript作为浏览器的脚本语言,起初是为了与用户交互和操作DOM,为了避免因为同时操作了同一DOM节点而引起冲突,被设计成为一种单线程语言。而单线程语言最大的特性就...

    nemo 评论0 收藏0

发表评论

0条评论

CKJOKER

|高级讲师

TA的文章

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