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
异步函数使用了async
和await
语法,这是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
是as soon as possible
的简称,在node和浏览器环境下,能将回调函数以高优先级任务来执行(下一个事件循环之前),即把任务放在微任务队列中执行。
宏任务(macro-task)和微任务(micro-task)表示异步任务的两种分类。在挂起任务时,js 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。
用法:
asap(function () { // ... });
4.参考
上一篇: 详解C#中委托,事件与回调函数讲解
下一篇: C#如何自动识别文件的编码