浏览器中的 Event Loop及异步代码执行顺序

当Event Loop遇到异步的代码时,会被挂起并在需要执行的时候加入到 Task(有多种 Task) 队列中。
一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。可以把 await 看成是让出线程的标志。
微任务包括 process.nextTick ,promise ,MutationObserver。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI

Event Loop 执行顺序如下所示

  1. 首先执行同步代码,这属于宏任务;
  2. 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行;
    执行所有微任务;
  3. 当执行完所有微任务后,如有必要会渲染页面;
  4. 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数。
    代码示例
	console.log('script start');//1.同步代码,宏任务
    async function async1() {
        await async2();//跳出,执行async2()
        console.log('async1 end');//5.异步代码,微任务
    }
    async function async2() {
        console.log('async2 end');//2.
    }
    async1();//同步代码,宏任务
    setTimeout(function() {
        console.log('setTimeout');//8.异步代码,宏任务
    }, 0);
    new Promise(resolve => {//同步代码,微任务
        console.log('Promise');//3.
        resolve()
    })
        .then(function() {
            console.log('promise1');//6.异步代码,微任务
        })
        .then(function() {
            console.log('promise2');//7.异步代码,微任务
        });
    console.log('script end');//4.//同步代码,宏任务
  1. 首先先来解释下上述代码的 async 和 await 的执行顺序。
  2. 当我们调用 async1 函数时,会马上输出 async2 end,并且函数返回一个 Promise。
  3. 接下来在遇到 await的时候会就让出线程开始执行 async1 外的代码,所以我们完全可以把 await 看成是让出线程的标志。
  4. 所以以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise 属于微任务而 setTimeout 属于宏任务,所以会有以上的打印。
    根据个人理解及部分资料得出以上结论,如有错误,请批评指正。

你可能感兴趣的:(浏览器)