async 函数是 Generator 函数的语法糖。使用 关键字 async 来表示,在函数内部使用 await 来表示异步。想较于 Generator,Async 函数的改进在于下面四点:promise
await命令:正常状况下,await命令后面是一个 Promise 对象,返回该对象的结果。若是不是 Promise 对象,就直接返回对应的值 浏览器
下面给你们看一道以前看过的题:网络
function test1() { console.log("执行test1"); return "test1"; } function test2() { console.log("执行test2"); return Promise.resolve("hello test2"); } async function asyncTest() { console.log("asyncTest start..."); const v1 = await test1(); console.log(v1); const v2 = await test2(); console.log(v2); console.log(v1, v2); } setTimeout(function(){ console.log('setTimeout') },0) asyncTest(); new Promise(function(resolve){ console.log('promise1') resolve(); }).then(function(){ console.log('promise2') }) console.log('test end')
这道题结合了setTimeout、async、promise异步函数,根据三种不一样异步任务执行顺序能够学习js引擎的事件循环机制,我们先看下结果:数据结构
test start... 执行test1 promise1 test end test1 执行test2 promise2 hello test2 test1,hello test2 setTimeout
再讲答案以前先理解如下几个概念:异步
事件循环与消息队列 async
JS引擎线程遇到异步(DOM事件监听、网络请求、setTimeout计时器等...),会交给相应的线程单独去维护异步任务,等待某个时机(计时器结束、网络请求成功、用户点击DOM),而后由 事件触发线程 将异步对应的 回调函数 加入到消息队列中,消息队列中的回调函数等待被执行。函数
同时,JS引擎线程会维护一个 执行栈,同步代码会依次加入执行栈而后执行,结束会退出执行栈。oop
若是执行栈里的任务执行完成,即执行栈为空的时候(即JS引擎线程空闲),事件触发线程才会从消息队列取出一个任务(即异步的回调函数)放入执行栈中执行。学习
消息队列是相似队列的数据结构,遵循**先入先出(FIFO)**的规则。
执行完了后,执行栈再次为空,事件触发线程会重复上一步操做,再取出一个消息队列中的任务,这种机制就被称为事件循环(event loop)机制。线程
主代码块(script)依次加入执行栈,依次执行,主代码块为:
宏任务与微任务
macrotask(宏任务) :主代码块、setTimeout、setInterval等(能够看到,事件队列中的每个事件都是一个 macrotask,如今称之为宏任务队列
和 microtask(微任务):Promise、process.nextTick等
JS引擎线程首先执行主代码块。
每次执行栈执行的代码就是一个宏任务,包括任务队列(宏任务队列)中的,由于执行栈中的宏任务执行完会去取任务队列(宏任务队列)中的任务加入执行栈中,即一样是事件循环的机制。
在执行宏任务时遇到Promise等,会建立微任务(.then()里面的回调),并加入到微任务队列队尾。
microtask必然是在某个宏任务执行的时候建立的,而在下一个宏任务开始以前,浏览器会对页面从新渲染(task >> 渲染 >> 下一个task(从任务队列中取一个))。同时,在上一个宏任务执行完成后,渲染页面以前,会执行当前微任务队列中的全部微任务。
也就是说,在某一个macrotask执行完后,在从新渲染与开始下一个宏任务以前,就会将在它执行期间产生的全部microtask都执行完毕(在渲染前)。
执行机制:
遇到异步函数 setTimeout,交给定时器触发线程 setTimeout加入宏任务队列,JS引擎线程继续,出栈;
执行异步函数asyncTest,首先打印test start...
执行await test1函数首先打印"执行test1",await让出线程去执行后面的代码;
执行Promise 首先打印promise1,then后面函数为微任务,添加到微任务队列中
JS引擎线程继续向下执行同步代码console.log('test end')打印'test end'
回到asyncTest执行await test1因为返回不是promise对象,因此直接返回test1
执行await test2()一样先打印 "执行test2",因为test2返回promise对象 会加入到以前微任务队列中,await继续让出
执行微任务队列,因为任务队列遵循先进先出结果,因此首先打印promise2,而后打印hello test2
微任务队列执行完成后继续执行asyncTest内 await以后的代码打印 俩个await返回的值 --test1,hello test2
最后回到宏任务队列执行setTimeout,打印setTimeout
若是我把test1变成异步函数,你们再思考一下会打印什么结果:
async function test1() { console.log("执行test1"); return "test1"; } function test2() { console.log("执行test2"); return Promise.resolve("hello test2"); } async function asyncTest() { console.log("asyncTest start..."); const v1 = await test1(); console.log(v1); const v2 = await test2(); console.log(v2); console.log(v1, v2); } setTimeout(function(){ console.log('setTimeout') },0) asyncTest(); new Promise(function(resolve){ console.log('promise1') resolve(); }).then(function(){ console.log('promise2') }) console.log('test end')
以上就是此代码执行过程,因为本人也是在学习总结中,若有不对的地方请指教,共同窗习,一块儿进步!!!