async和await是如何实现异步编程?

浅谈Promise如何实现异步执行

参考


1.异步编程样例

样例:

// 等待执行函数 function sleep(timeout) { return new Promise((resolve) => { setTimeout(resolve, timeout) }) } // 异步函数 async function test() { console.log('test start') await sleep(1000) console.log('test end') } console.log('start') test() console.log('end')

执行结果:

start test start end test end

2.样例解析

在样例代码中,test异步函数使用了asyncawait语法,这是ES2017里面的异步编程规范。而为了在较低版本的浏览器或Node支持这种语法,其中一种解决方案是将其转化为Generator函数和Promise来实现。换句话说,任何的async和await实现的异步函数,都可以替换成Generator函数和Promise实现。

第一步:先将async和await语法替换为相应的Generator 函数,如下

// 代码结构完全一致,只是替换了对应关键字 function *test() { console.log('test start') yield sleep(1000) console.log('test end') }

第二步:为了执行Generator 函数,使用Promise实现一个自动执行器函数 spawn

function spawn(genF) { return new Promise(function(resolve, reject) { const gen = genF(); function step(nextF) { let next; try { next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); }

第三步:将相应的Generator 函数和自动执行器函数相结合,即可得最终的等价代码

function asyncTest() { spawn(test) } console.log('start') asyncTest() console.log('end')

第四步:执行结果,与原来的一致

start test start end test end

第五步:分析asyncTest函数执行过程

执行到 spawn(test),进入spawn函数中,创建一个Promise并返回。

执行到 const gen = genF(),获取一个状态机。(Generator 函数是一个状态机,封装了多个内部状态。)

执行到 step(function() { return gen.next(undefined); }), 进入step 函数中。

执行到next = nextF() ,next等于gen.next(undefined)的返回结果。

当gen.next(undefined)开始执行,状态机第一次调用,直到遇到第一个yield表达式为止,即yield sleep(1000),此时控制台先输出"test start",并且返回第一个状态{ value: reuslt, done: false }, 而 reuslt等于sleep(1000)返回的结果,其是一个Promise。

执行到if(next.done) ,此时第一个状态的done为false,所以不执行if语句里面,继续往下执行。

执行到Promise.resolve(next.value),由于第一个状态的value是一个Promise,所以直接返回其本身,也就相当于执行sleep(1000).then(...),sleep函数异步等待1秒后,resolve接受的值为undefined,继续执行then方法。

执行到 step(function() { return gen.next(v); }),此时v为undefined,再次进入step 函数中。

再次执行到next = nextF() ,next等于gen.next(undefined)的返回结果。

当gen.next(undefined)再次执行时,状态机第二次调用,此时Generator函数已经执行完毕,此时控制台先输出"test end",并且返回最后的状态{ value: undefined, done: true }

执行到if(next.done) ,此时第一个状态的done为true,所以执行if语句里面。

执行到return resolve(next.value),此时最初的Promise成功执行。

至此asyncTest函数执行结束。


3.浅谈Promise如何实现异步执行

从上述样例解析中可以看出,我们是用Promise来实现代码的异步执行,那Promise的内部是如何实现异步执行的呢?

通过查看Promise的源码实现,发现其异步执行是通过asap这个库来实现的。

asap是as soon as possible的简称,在Node和浏览器环境下,能将回调函数以高优先级任务来执行(下一个事件循环之前),即把任务放在微任务队列中执行。

宏任务(macro-task)和微任务(micro-task)表示异步任务的两种分类。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

用法:

asap(function () { // ... });

4.参考

【翻译】Promises/A+规范

Promise - Bare bones Promises/A+ implementation

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwgwp.html