惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
爱范儿
爱范儿
Y
Y Combinator Blog
T
Tor Project blog
V
Visual Studio Blog
U
Unit 42
B
Blog RSS Feed
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
G
Google Developers Blog
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
IT之家
IT之家
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
Google DeepMind News
Google DeepMind News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
H
Hackread – Cybersecurity News, Data Breaches, AI and More
F
Fortinet All Blogs
人人都是产品经理
人人都是产品经理
Apple Machine Learning Research
Apple Machine Learning Research
The GitHub Blog
The GitHub Blog
Recorded Future
Recorded Future
博客园_首页
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
P
Proofpoint News Feed
Jina AI
Jina AI
博客园 - 【当耐特】
S
Security @ Cisco Blogs
I
Intezer
MyScale Blog
MyScale Blog
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy & Cybersecurity Law Blog
腾讯CDC
T
Tenable Blog
A
Arctic Wolf
T
Threat Research - Cisco Blogs
S
Securelist
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
C
Check Point Blog
NISL@THU
NISL@THU
Microsoft Security Blog
Microsoft Security Blog
V
Vulnerabilities – Threatpost

DaraW

GMTC 北京 2021 小程序开发实践专场所见所想 从一次 Bug 定位来看 Taro H5 的组件实现 浅谈 B 站新 BV 号的设计 复盘:Vue.js 是如何成功的 你可能不知道的 Git React 应用性能优化一例 Thrift RPC Mock 方案探索 前端视频质量监控 前端函数式编程 多说一句再见 《你不知道的JS上卷》阅读小记之setTimeout的this指向问题 ES6实现内部类的写法 IntelliJ IDEA在Linux下字体不正常解决方案 如何监听JS变量的变化 JS实现自定义事件 制作列带有斑马条纹背景的表格 git用户名莫名其妙变化及挽救措施 记一次前端性能优化实战 将setTimeout函数队列
node-thunkify源码阅读笔记
DaraW · 2016-11-12 · via DaraW

Node7发布后已经可以通过添加--harmony-async-await的参数调用来直接支持async/await语法了,据说Node8还会进一步推进其发展,于是研究了一下JS的异步流程控制和下一代Node Web框架Koa2

关于generator async/await的发展史已有一大堆文章讲过了,这里不再赘述。
tj的coKoa2上个大版本Koa1的核心,在没有async/await的时候一般会借助co来做自动流程控制。关于co的源码分析文章也有很多,代码不长值得一读,参考了一些分析文章也算是了解了其逻辑和思路。

co中出现了一个thunkToPromise的函数,一些文章都跳过了这个并表示thunk函数已经没什么意义了,但本着好奇心读了阮一峰的Thunk 函数的含义和用法,文中一个地方一时没有搞懂,故写此文记录一下。

thunkify代码很少,就是一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function thunkify(fn){
assert('function' == typeof fn, 'function required');

return function(){
var args = new Array(arguments.length);
var ctx = this;

for(var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}

return function(done){
var called;

args.push(function(){
if (called) return;
called = true;
done.apply(null, arguments);
});

try {
fn.apply(ctx, args);
} catch (err) {
done(err);
}
}
}
};

Demo:

1
2
3
4
5
6
7
8
9
function f(a, b, callback){
var sum = a + b;
callback(sum);
callback(sum);
}

var ft = thunkify(f);
ft(1, 2)(console.log);

让我一时没有搞懂的是为什么借助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数组中有两个成员即12ft最后又返回了一个闭包,这里称之为闭包2。
接着执行的是ft(1, 2)(console.log),也就是将console.log传入闭包2来执行,传入的console.log即形参done其实就是一开始f的回调函数,这时候重点来了,在闭包2中增加了一个标记called来记录回调是否执行过一次了,而pushargs数组的函数则已经不是单纯的回调,而是被包裹了原回调、保证只会执行一次的函数

1
2
3
4
5
function(){
if (called) return;
called = true;
done.apply(null, arguments);
}

最后执行的就是fn.apply(ctx, args),而thunkify函数的形参fn对应的就是一开始传入的f函数,所以总的看来,真正执行的还是最初的f函数,而被改变的是传入的回调,回调被包裹了一层,借助called标记来保证只会被执行一次,执行过后called标记被改变,不再会被执行。