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

事件循环机制

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

简单版本

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

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

咱们能够将js的任务分为同步任务异步任务, 按照这种分类js的执行机制以下浏览器

  • 任务执行队列分为同步任务队列异步任务队列
  • 代码执行时,遇到同步代码,会被直接推入同步任务队列并依次执行
  • 遇到异步代码(如setTimeout、setInterval), 会被直接推入异步任务队列
  • 同步任务队列执行完毕,这个时候异步任务队列的任务会被依次推入同步任务队列并依次执行

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

终极版本

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

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()的时候是同步,当即执行。

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

因此针对这种机制,js的事件循环机制应该是这样的code

  • 遇到同步代码,依次推入同步队列并执行
  • 当遇到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. 遇到同步任务先输出1。
  2. setTimeout是宏任务,会先放到宏任务队列中。
  3. new Promise是当即执行的,因此会先输出3。
  4. Promise.then是微任务,会依次排列到微任务队列中,继续向下执行输出2。
  5. 如今执行栈中的任务已经清空,再将微任务队列清空,依次输出100和200。
  6. 而后每次取出一个宏任务,由于如今只有一个宏任务,因此最后输出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浏览器上输出的结果
clipboard.png
使用事件循环机制分析:

  1. 首先执行同步代码,console.log( 'script start' )
  2. 遇到setTimeout,会被推入宏任务队列
  3. 执行async1(), 它也是同步的,只是返回值是Promise,在内部首先执行console.log( 'async1 start' )
  4. 而后执行async2(), 而后会打印console.log( 'async2' )
  5. 从右到左会执行, 当遇到await的时候,阻塞后面的代码,去外部执行同步代码
  6. 进入 new Promise,打印console.log( 'promise1' )
  7. .then放入事件循环的微任务队列
  8. 继续执行,打印console.log( 'script end' )
  9. 外部同步代码执行完毕,接着回到async1()内部, 因为async2()实际上是返回一个Promise, await async2()至关于获取它的值,其实就至关于这段代码Promise.resolve(undefined).then((undefined) => {}),因此.then会被推入微任务队列, 因此如今微任务队列会有两个任务。接下来处理微任务队列,打印console.log( 'promise2' ),后面一个.then不会有任何打印,可是会执行
  10. 执行后面的代码, 打印console.log( 'async1 end' )
  11. 进入第二次事件循环,执行宏任务队列, 打印console.log( 'setTimeout' )
相关文章
相关标签/搜索