咱们知道,JavaScript做为浏览器的脚本语言,起初是为了与用户交互和操做DOM,为了不由于同时操做了同一DOM节点而引发冲突,被设计成为一种单线程语言。而单线程语言最大的特性就是同一时间只能作一件事,这个任务未完成下一个任务就要等待,这样无疑是对资源的极大浪费,并且严重时会引发阻塞,形成用户体验极差。这个时候就引出了异步的概念,而异步的核心就是事件循环机制Event Loop。node
JavaScript的任务分两种,分别是同步任务和异步任务。segmentfault
如上图所示:promise
不断重复以上步骤,就造成了事件循环(Event Loop)浏览器
<script> console.log('start') setTimeout(function () { console.log('setTimeout') }, 0) console.log('end') </script>
结合上面步骤分析下这个例子:异步
1. 执行主线程同步任务,输出start【1】,继续往下执行 2. 遇到setTimeout,进入event table注册setTimeout回调,setTimeout回调执行完后,继续往下执行 3. 输出end【2】,同步任务执行完毕 4. 进入event queue,检查是否有可执行任务,取出event queue中setTimeout任务开始执行,输出setTimeout【3】
结果依次为:start -> end -> setTimeoutasync
在浏览器和node中的事件循环与执行机制是不一样的,要注意区分,不要搞混。
浏览器环境的异步任务分为宏任务(macroTask)和微任务(microtask),当知足条件时会分别被放进宏任务队列和微任务队列(先进先出),等待被执行。函数
执行过程以下:oop
如图所示:post
1. 把总体的script代码做为宏任务执行 2. 执行过程当中若是遇到宏任务和微任务,知足条件时分别添加至宏任务队列和微任务队列 3. 执行完一个宏任务后,取出全部微任务依次执行,若是微任务一直有新的被添加进来,则一直执行,直到把微任务队列清空 4. 不断重复2和3,直到全部任务被清空,结束执行。
<script> console.log('start') setTimeout(() => { console.log('timer1') Promise.resolve().then(() => { console.log('promise1') }) }, 0) setTimeout(() => { console.log('timer2') Promise.resolve().then(() => { console.log('promise2') }) }, 0) setTimeout(() => { console.log('timer3') Promise.resolve().then(() => { console.log('promise3') }) }, 0) new Promise(function(resolve) { console.log('promise4'); resolve(); }).then(function() { console.log('promise5') }) console.log('end') </script>
分析:测试
第一轮:
第二轮:
第三轮:
第四轮:
如今宏任务对列和微任务队列都被清空了,完成执行,结果为:start > promise4 > end > promise5 > timer1 > promise1 > timer2 > promise2 > timer3 > promise3
await表达式的运算结果取决于它右侧的结果
当遇到await时,会阻塞函数体内部处于await后面的代码,跳出去执行该函数外部的同步代码,当外部同步代码执行完毕,再回到该函数内部执行剩余的代码
补充aynsc的一点知识:若是aynsc函数中return一个直接量,async 会把这个直接量经过Promise.resolve()封装成Promise对象,若是什么都没return,会被封装成Promise.resolve(undefined)
那么 引入了async await以后的执行过程是怎样的呢?
<script> 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"); </script>
分析:
第一轮:
进入aynsc1函数中,执行同步代码输出:async1 start【2】,遇到await从右向左执行,进入async2函数,输出:async2【3】;aynsc2函数体中未返回任何东西等价于返回了Promise.resolve(undefined)
,拿到返回值后进入aynsc1函数体中,继续执行剩下的部分,这时候aynsc1中注释部分等价于:
async function async1() { console.log("async1 start"); //await async2(); //console.log("async1 end"); await new Promise((resolve) => resolve()).then(resolve => { console.log('async1 end') }) }
将Promise.then@1推入到微任务队列;
第二轮:
全部任务队列均为空,结束执行,输出结果为:script start > async1 start > async2 > promise1 > script end > async1 end > promise2 > setTimeout
补充谷歌浏览器测试结果:
借用一个例子:await一个直接值的状况
<script> console.log('1') async function async1() { console.log('2') await 'await的结果' console.log('5') } async1() console.log('3') new Promise(function (resolve) { console.log('4') resolve() }).then(function () { console.log('6') }) </script>
分析:
第一轮:
任务队列为空,执行完毕,结果为: 1 > 2 > 3 > 4 > 5 > 6
再借个例子,这个有点复杂
<script> setTimeout(function () { console.log('8') }, 0) async function async1() { console.log('1') const data = await async2() console.log('6') return data } async function async2() { return new Promise(resolve => { console.log('2') resolve('async2的结果') }).then(data => { console.log('4') return data }) } async1().then(data => { console.log('7') console.log(data) }) new Promise(function (resolve) { console.log('3') resolve() }).then(function () { console.log('5') }) </script>
分析:
第一轮:
开始执行第一轮微任务,取出Promise.then@1,输出:4【4】,此时async2函数执行完毕,进入aynsc1函数,此时改动下aynsc1函数,等价于:
async function async1() { console.log('1') //const data = await async2() //console.log('6') const data = await new Promise(resolve => resolve('async2的结果')).then((resolve) => { console.log(6); return resolve; }) return data; }
将上面promise.then@3推入微任务队列中,此时:
async1().then(...)
,将async1().then@1推到微任务队列中,取出async1().then@1,输出:7【7】和 'async2的结果'【8】;第二轮:
因此任务被执行完毕,结果为:1 > 2 > 3 > 4 > 5 > 6 > 7 > async2的结果 > 8
------------------------ END ----------------------------
PS: 好记性不如烂笔头,看了那么多资料,仍是想总结一下,否则过一阵子就忘记了,若是辛苦指出哦,谢谢~
参考资料:
理解 JavaScript 的 async/await
浏览器和Node不一样的事件循环(Event Loop)
Event Loop 原来是这么回事
这一次,完全弄懂 JavaScript 执行机制
从event loop到async await来了解事件循环机制...