目录
异步编程样例
样例解析
浅谈 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 的源码 https://github.com/then/promise 实现, 发现其异步执行是通过 https://github.com/kriskowal/asap 这个库来实现的.
asap 是 as soon as possible 的简称, 在 Node 和浏览器环境下, 能将回调函数以高优先级任务来执行(下一个事件循环之前), 即把任务放在微任务队列中执行.
宏任务 (macro-task) 和微任务 (micro-task) 表示异步任务的两种分类. 在挂起任务时, JS 引擎会将所有任务按照类别分到这两个队列中, 首先在 macrotask 的队列 (这个队列也被叫做 task queue) 中取出第一个任务, 执行完毕后取出 microtask 队列中的所有任务顺序执行; 之后再取 macrotask 任务, 周而复始, 直至两个队列的任务都取完.
用法:
- asap(function () {
- // ...
- });
4. 参考
ECMAScript 6 入门 - async 函数 https://es6.ruanyifeng.com/#docs/async
[翻译] Promises/A + 规范 https://www.ituring.com.cn/article/66566
Promise - Bare bones Promises/A+ implementation https://github.com/then/promise
来源: https://www.cnblogs.com/forcheng/p/12668387.html