Node7发布后已经可以通过添加--harmony-async-await的参数调用来直接支持async/await语法了,据说Node8还会进一步推进其发展,于是研究了一下JS的异步流程控制和下一代Node Web框架Koa2。
关于generator async/await的发展史已有一大堆文章讲过了,这里不再赘述。
tj的co是Koa2上个大版本Koa1的核心,在没有async/await的时候一般会借助co来做自动流程控制。关于co的源码分析文章也有很多,代码不长值得一读,参考了一些分析文章也算是了解了其逻辑和思路。
在co中出现了一个thunkToPromise的函数,一些文章都跳过了这个并表示thunk函数已经没什么意义了,但本着好奇心读了阮一峰的Thunk 函数的含义和用法,文中一个地方一时没有搞懂,故写此文记录一下。
thunkify的代码很少,就是一个函数:
1 | function thunkify(fn){ |
Demo:
1 | function f(a, b, callback){ |
让我一时没有搞懂的是为什么借助called标记能够确保回调函数只执行一次,借助VS Code的断点调试把文中示例的代码跑了一遍总算搞懂了:
Demo中首先真正执行的是thunkify(f),f函数传入thunkify后直接返回了一个闭包,这里称之为闭包1,闭包1被赋值给了ft,ft即为闭包1的一个引用。
接着执行的是ft(1, 2),ft中传入了(1, 2)来执行,ft中将duck type的伪数组arguments保存为一个真实数组args,所以此时args数组中有两个成员即1和2,ft最后又返回了一个闭包,这里称之为闭包2。
接着执行的是ft(1, 2)(console.log),也就是将console.log传入闭包2来执行,传入的console.log即形参done其实就是一开始f的回调函数,这时候重点来了,在闭包2中增加了一个标记called来记录回调是否执行过一次了,而push进args数组的函数则已经不是单纯的回调,而是被包裹了原回调、保证只会执行一次的函数
1 | function(){ |
最后执行的就是fn.apply(ctx, args),而thunkify函数的形参fn对应的就是一开始传入的f函数,所以总的看来,真正执行的还是最初的f函数,而被改变的是传入的回调,回调被包裹了一层,借助called标记来保证只会被执行一次,执行过后called标记被改变,不再会被执行。