Promise 从入门到实战

JavaScript 编写异步代码

  • 回调函数
  • 事件监听
  • 发布订阅,消息中心
  • Promise/A+
  • Generator
  • async/await
  • Thunk

Promise 示例

Promise 值具有穿透性,穿透一个没有用的 .then() 直接到达 一个有用的 .then()

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
let p1 = new Promise((resolve,reject) => {
resolve(1)
})

p1.then()
.then(value => {
console.log(value);
return value + 1
}).then(value => {
console.log(value);
})

// output: 1 2

// 两者不等价
let p2 = Promise.resolve(1);
p2.then(v => { v + 1 })
.then(v => { console.log(v)});

// output: 2

let p3 = Promise.resolve(1);
p3.then(v => v + 1);
p3.then(v => console.log(v));

// output: 1

let p4 = Promise.resolve(1);
p4.then(v => {
return new Promise((resolve,reject) => {
setTimeout(() => resolve(2))
})
}).then(v => console.log(v));

// output: 2

let p1 = new Promise((resolve , reject) => {
reject(new Error ('some wrong'))
})

let p2 = p1.then(
value => { console.log(value) },
reason => { console.log(reason.message) }
)

// output: something wrong

p2.then(()=>{
throw new Error('something wrong 1')
}).catch(reason => {
console.log(reason.message)
})

// output: something wrong 1

Promise.reject(new Error('error')))
.catch(reason => {
console.log(reason.message);
return `${reason.message} occured`
}).then(v => console.log(v));

// output: error error occured

function sleep(duration){
return new Promise((resolve,reject) =>{
setTimeout(() => {resolve(),duration})
})
}

async function run(){
const start = Date.now();
await sleep(1000);
await sleep(2000);
await sleep(3000);
console.log(Date.now() - start)
}

run()
// ==== 以下是 promise 的书写方法 === //
function run1(){
const start = Date.now();
return Promise.resolve()
.then(function() { return sleep(1000)})
.then(function() { return sleep(2000)})
.then(function() { return sleep(3000)})
.then(function() { console.log(Date.now() - start)})
}

run1();

实现 Promise 需要注意

  • 符合 Promise/A+ 标准
  • 不暴露过多的函数和变量
  • Promise 状态只能修改一次
  • then() 和 catch 需要异步执行
  • then 能够实现链式调用
  • then 返回新的 Promise
  • then 值能够穿透
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//  Promise 状态有 3 个  
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
class Promise {
constructor(fn){
// 初始状态
this._state = PENDING;
this._data = null;

this._onFulfilledCallbacks = [];
this._onRejectedCallbakcs = [];

run(this,fn);
}

static deferred (){
const dfd = {}
dfd.promise = new promise ((resonlve,reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}

then(onFulfilled, onRejected){
if(typeof onFulfilled !== 'function'){
onFulfilled = function(data){
return data
}
}
if(typeof onRejected !== 'function'){
onRejected = function(reason){
throw reason
}
}

let promise2;

if(this._state === FULFILLED){
promise2 = new Promise((resolve,reject ) =>{
setTimeout(()=>{
try{
const x = onFulfilled(this._data)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e);
}
})
})
}else if(this._state === REJECTED){
promise2 = new Promise((resolve,reject ) =>{
setTimeout(()=>{
try{
const x = onRejected(this._data)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e);
}
})
})

}else if(this._state === PENDING){
promise2 = new Promise((resolve,reject)=>{
this._onFulfilledCallbacks.push(data => {
try{
const x = onFulfilled(data)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
this._onRejectedCallbakcs.push(reason =>{
try{
const x = onRejected (reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
})
}
}
return promise2
}

catch(onRejected){
return this.then(undefined,onRejected)
}

function resolvePromise (promise2,x,resolve,reject){
if(x instanceof Promise){
return x.then(resolve,reject);
}else{
resolve(x)
}
}
function run (promise,fn){
try{
fn(
data => {
// do resolve
resolve(promise,data)
},
reason => {
// do reject
reject(promise,reason)
}
)
}catch(e){
// reject
reject(promise,e)
}
}

function resolve(promise,data) {
// 处理值是 Promise 的情况
if(data instanceof Promise){
return data.then(
d => { resolve (promise,d)},
r => { reject (promise,r)}
)
}
if(promise._state !== PENDING){
callback(data)
}

setTimeout(()=>{
promise._state = FULFILLED;
promise._data = data;

for(let callback of promise._onFulfilledCallbacks) {
callback(data)
}
})

}

function reject(promise,reason) {
if(promise._state !== PENDING){
return
}

setTimeout(()=>{
promise_state = REJECTED
promise_data = reason

for (let callback of promise__onRejectedCallbakcs){
callback(reason )
}
})
}

module.exports = Promise;

校验 是否符合 promise 规范

promises-aplus-tests

Promise 是什么

按照用途来解释

  • 主要用于异步计算。
  • 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。
  • 可以在对象之间传递和操作 Promise,帮助我们处理队列。

为什么会有 Promise

JavaScript 包含大量异步操作

JavaScript 为检查表单而生。
创造它的首要目标是操作 DOM。
所以,JavaScript 的操作大多是异步的。
是 UI 语言造成界面冻结是大忌

为什么异步操作可以避免界面冻结

同步问题:按顺序执行,按顺序完成。

异步:完成顺序和执行顺序无关。

异步操作的常见语法

事件监听与响应:

1
2
3
4
5
6
7
8
  document.getElementById("start").addEventListener("click", start, false);

function start() {
// 响应事件,进行相应的操作
}

// jQuery 用 '.on()' 也是事件监听
$("#start").on("click", start);

异步操作的常见语法

1
2
3
4
5
6
7
8
9
10
$.ajax("http://baidu.com", {
success: function (res) {
// 这里就是回到函数了
},
});

// 或者在页面加载完毕后回调
$(function () {
// 这里也是回调函数
});

浏览器中的 JavaScript

  • 异步操作以事件为主
  • 回调主要出现在 Ajax 和 File API

Node.js 的出现

对异步的依赖进一步加剧了

  • 无阻赛高并发,是 Node.js 的招牌。
  • 异步操作是其保障。
  • 大量操作依赖回调函数。

异步回调的问题

  • 嵌套层次很深,难以维护
  • 无法正常使用 returnthrow
  • 无法正常检索堆栈信息
  • 多个回调之间难以建立联系

Promise 详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new Promise(
/* 执行器 executor */
function (resolve, reject) {
// 一段耗时很长的异步操作

resolve(); // 数据处理完成

reject(); // 数据处理出 错
}
).then(
function A() {
// 成功,下一步
},
function B() {
// 失败,做相应处理
}
);

Promise 是一个代理对象,它和原先要进行的操作并无关系。
它通过引入一个回调,避免更多的回调。

Promise 有 3 个状态

pending [待定] 初始状态
fulfilled [实现] 操作成功
reject [被否定] 操作失败

Promise 状态发生改变,就会触发 .then() 里的响应函数处理后续步骤。
Promise 状态一经改变,不会再变。

Promise 执行顺序

范例

1
2
3
4
5
6
7
new Promise((resolve) => {
setTimeout(() => {
resolve("hello");
}, 2000);
}).then((value) => {
console.log(value + "world");
});

两步执行范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 两步执行范例
new Promise( resolve =>{
setTimeout(()=>{
resolve('hello');
},2000);
}).then( value => {
console.log(value);
return new Promise( resolve =>{
setTimeout(() => {
resolve('world');
},2000);
});
}).then( value =>{
console.log( value + 'world');
})

对已完成的 promise 执行

1
2
3
4
5
6
7
8
9
10
11
12
13
// 对已完成的 promise 执行
let promise = new Promise(resolve =>{
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello,world');
},1000)
})

setTimeout(()=>{
promise.then( value =>{
console.log(value);
})
},3000)

then 里不返回 promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// then 里不返回 promise
new Promise( resolve =>{
setTimeout(() =>{
resolve('hello');
},2000);
}).then(value =>{
console.log(value);
console.log('everyone');
(function (){
return new Promise(resolve => {
setTimeout(()=>{
console.log('Mr.Laurence');
resolve(' Merry Xmas ');
},2000)
});
}());
return false;
}).then( value =>{
console.log(value + ' world');
})

.then()

  • .then() 接受两个函数作为参数,分别代表 fulfilled 和 rejected
  • .then() 返回一个新的 Promise 实例,所以它可以链式调用
  • 当前面的 Promise 状态改变时,.then() 根据其最终状态,选择特定的状态响应函数执行
  • 状态响应函数可以返回新的 Promise,或其他值
  • 如果返回新的 Promise,那么下一级 .then() 会在新 Promise 状态改变之后执行。
  • 如果返回其他任何值,则会立刻执行下一级 .then()

.then()里有 .then()的情况

  • 因为 .then() 返回的还是 Promise 实例。
  • 会等里面的 .then() 执行完,再执行外面的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
console.log('start')
new Promise( resolve =>{
console.log('Step 1');
setTimeout(() =>{
resolve('1000');
},1000);
}).then(value =>{
return new Promise( resolve =>{
console.log('Step 1-1');
setTimeout(()=>{
resolve(110);
},1000)
}).then( value => {
console.log('Step 1-2');
return value;
}).then(value => {
console.log('Step 1-3');
return value;
})
}).then( value =>{
console.log(value);
console.log('Step 2');
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 展开
console.log('start')
new Promise( resolve =>{
console.log('Step 1');
setTimeout(() =>{
resolve('1000');
},1000);
}).then(value =>{
return new Promise( resolve =>{
console.log('Step 1-1');
setTimeout(()=>{
resolve(110);
},1000)
})
}).then( value => {
console.log('Step 1-2');
return value;
}).then(value => {
console.log('Step 1-3');
return value;
}).then( value =>{
console.log(value);
console.log('Step 2');
})

下面 Promise 的区别

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
doSomething().then(function(){
return doSomethingElse()
});


// doSomething().then(function(){
// return doSomethingElse();
// }).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|


doSomething().then(function(){
doSomethingElse()
});

// doSomething().then(function(){
// doSomethingElse();
// }).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|

doSomething().then(
doSomethingElse()
);

// doSomething().then(
// doSomethingElse();
// ).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |----------------------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|


doSomething().then(
return doSomethingElse
);

// doSomething().then(
// doSomethingElse;
// ).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(resultOfDosomething)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|

错误处理

Promise 会自动捕获内部异常,并交给 rejected 响应函数处理。
错误处理两种做法:

  • reject(‘错误信息’).then(null,message =>{})
  • throw new Error(‘错误信息’).catch( message => {})

推荐第二种,更加清晰好读,并且可以捕获前面的错误。

1
2
3
4
5
6
7
8
9
new Promise( resolve =>{
setTimeout(()=>{
throw new Error('bye');
},2000);
}).then( value =>{
console.log(value + 'world');
}).catch( error =>{
console.log( 'Error:',error.message )
})
1
2
3
4
5
6
7
8
9
new Promise( (resolve,reject) =>{
setTimeout(()=>{
reject('bye');
},2000);
}).then( value =>{
console.log(value + 'world');
},value =>{
console.log('Error',value);
})

.catch() + .then()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new Promise(resolve =>{
setTimeout(()=>{
resolve();
},1000)
}).then(()=>{
console.log('start');
throw new Error('test error');
}).catch( err => {
console.log('I catch ',err);
// throw new Error('another error');
}).then(()=>{
console.log('arrive here');
}).then(()=>{
console.log('...and here');
}).catch( err =>{
console.log('No,I catch',err);
})
注意: 强烈建议在所有队列最后都加上 .catch(),以避免漏掉错误处理造成意想不到的问题。
1
2
3
4
5
6
doSomething()
.doAnotherThing()
.doMoreThing()
.catch( err =>{
console.log(err);
})

Promise 常用函数

Promise.all()

批量执行

  • Promise.all([p1,p2,p3,…]) 用于将多个 Promise 实例,包装成一个新的 Promise 实例
  • 返回的实例就是普通 Promise
  • 接收一个数组作为参数。
  • 数组里可以是 Promise 对象,也可以是别的值,只有 Promise 会等待状态改变。
  • 当所有子 Promise 都完成,该 Promise 完成,返回值是全部值的数组
  • 有任何一个失败,该 Promise 失败,返回值是第一个失败的子 Promise 的结果。
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
29
30
31
32
33
34
35
36
37
38
39
40
Promise.all([1,2,3]).then( all => {
console.log('1: ',all);
return Promise.all([function(){
console.log('ooxx');
},'xxoo',false]);
}).then(all =>{
console.log('2: ',all);
let p1 = new Promise( resolve =>{
setTimeout(()=>{
resolve('I\'m P1');
},1500)
})
let p2 = new Promise( resolve =>{
setTimeout(()=>{
resolve('I\'m P2');
},1450)
})
return Promise.all([p1,p2]);
}).then( all =>{
console.log('3: ',all);
let p1 = new Promise( resolve =>{
setTimeout(() => {
resolve('I\'m P1');
},1500)
})
let p2 = new Promise( (resolve,reject) =>{
setTimeout(() => {
reject('I\'m P2');
},1000)
})
let p3 = new Promise( (resolve,reject) =>{
setTimeout(() => {
reject('I\'m P3');
},3000)
})
}).then( all =>{
console.log('all',all);
}).catch(err =>{
console.log(err);
})

Promise.add() 和 .map() 连用

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
29
30
const fs = require('fs');
const path = require('path');
const FileSystem = require('./FileSystem');

function findLargest(dir) {
return FileSystem.readDir(dir, 'utf-8').then( files => {
return Promise.all( files.map( file => {
return new Promise (resolve => {
fs.stat(path.join(dir, file), (err, stat) => {
if (err) throw err;
if (stat.isDirectory()) {
return resolve({
size: 0
});
}
stat.file = file;
resolve(stat);
});
});
}));
}).then( stats => {
let biggest = stats.reduce( (memo, stat) => {
if(memo.size < stat.size) {
return stat;
}
return memo;
});
return biggest.file;
})
}

实现队列

使用 .forEach()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function queue(things){
let promise = Promise.resolve();
things.forEach( thing =>{
promise = promise.then(()=>{
return new Promise( resolve =>{
doThing(thing,()=>{
resolve();
});
})
})
})
return promise;
}
queue('lots','of','things',...)
常见错误: 没有把 .then()产生的新 Promise 实例赋给 promise,没有生成队列。

使用 .reduce()

1
2
3
4
5
6
7
8
9
10
11
12
function queue(things){
return things.reduce((promise,thing) => {
return promise.then(() => {
return new Promise( resolve => {
doThing(thing,()=>{
resolve();
});
});
});
},Promise.resolve());
}
queue('lots','of','things',...)
常见错误: Promise 实例创建之后,会立刻运行执行器代码,所以这个也无法达成队列的效果。

半成品爬虫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let url = ['http://blog.meathill.com/'];
function fetchAll(urls) {
return urls.reduce((promise, url) => {
return promise.then( () => {
return fetch(url);
});
}, Promise.resolve());
}
function fetch(url) {
return spider.fetch(url)
.then( content => {
return saveOrOther(content);
})
.then( content => {
let links = spider.findLinks(content);
return fetchAll(links);
});
}
fetchAll(url);

Promise.resolve()

返回一个 fulfilled 的 Promise 实例,或原始 Promise 实例。

  • 参数为空,返回一个状态为 fulfilled 的 Promise 实例。
  • 参数是一个跟 Promise 无关的值,同上,不过 fulfilled 响应函数会得到这个参数
  • 参数为 Promise 实例,则返回该实例,不做任何修改
  • 参数为 thenable,立刻执行它的 .then()。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Promise.resolve().then( (value) => {
console.log('Step 1', value);
return Promise.resolve('Hello');
}).then( value =>{
console.log(value,'World');
return Promise.resolve(new Promise( resolve => {
setTimeout(()=>{
resolve('Good');
},2000);
}));
}).then( value => {
console.log(value, 'evening');
return Promise.resolve({
then(){
console.log(', everyone');
}
})
})

Promise.reject()

返回一个 rejected 的 Promise 实例。

Promise.reject() 不认 thenable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let promise = Promise.reject('something wrong');

promise.then( () => {
console.log('it\'s ok');
}).catch( () => {
console.log('no, it\'s not ok');
return Promise.reject({
then() {
console.log('it will be ok');
},
catch() {
console.log('not yet');
}
});
});

Promise.race()

类似 Promise.all(),区别在于它有任意一个完成就算完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let p1 = new Promise(resolve => {
// 这是一个长时间的调用
setTimeout(() => {
resolve('I\'m P1');
}, 10000);
});

let p2 = new Promise(resolve => {
// 这是个稍短的调用
setTimeout(() => {
resolve('I\'m P2');
}, 2000)
});

Promise.race([p1, p2]).then(value => {
console.log(value);
});

常见用法:

  • 把异步操作和定时器放在一起
  • 如果定时器先触发,就认为超时,告知用户

实际开发的 Promise

异步回调包装成 Promise

优点:

  • 可读性更好
  • 返回的结果可以加入任何 Promise 队列
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
29
30
31
32
33
const fs = require('fs');

module.exports = {
// 读取目录
readDir: function (path, options) {
return new Promise( resolve => {
fs.readdir(path, options, (err, files) => {
if (err) {
throw err;
}
resolve(files);
});
});
},
// 读取文件
readFile: function (path, options) {
return new Promise( resolve => {
fs.readFile(path, options, (err, content) => {
if (err) {
throw err;
}
resolve(content);
});
});
}
};

// 使用
const fs = require('./FileSystem');

fs.readFile('../README.md','utf-8').then(content =>{
console.log(content);
})

把任意异步操作包装成 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 弹出窗体
let confirm = popupManager.confirm('您确定么?');
confirm.promise.then(() => {
// do confirm staff
})
.catch(() => {
// do cancel staff
});

// 窗体的构造函数
class Confirm {
constructor() {
this.promise = new Promise( (resolve, reject) => {
this.confirmButton.onClick = resolve;
this.cancelButton.onClick = reject;
})
}
}

两步执行

1
2
3
4
5
6
7
8
9
10
11
12
13
// 对已完成的 promise 执行
let promise = new Promise(resolve =>{
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello,world');
},1000)
})

setTimeout(()=>{
promise.then( value =>{
console.log(value);
})
},3000)

实际开发会对请求进行 promise 化,两种情况

返回的数据直接更改值

1
2
3
4
5
6
7
8
9
10
getlist(data){
this.api.getlist(data).then(res=>{
this.list = res.data.data;
})
}

// 下拉刷新
pullDown(){
this.getlist(data);
}

返回数据传递给下个调用的函数

1
2
3
4
5
6
7
8
9
10
11
12
getlist(data){
return this.api.getlist(data).then(res=>{
return Promise.resolve(res.data.data);
})
}

// 模糊搜索
querySearchAsync(){
this.getlist(data).then((value)=>{
this.searchlist = value;
})
}

jQuery

jQuery 已经实现了 Promise。1.5 版开始尝试, 3.0版完成的 Promise。

参考 jQuery 的 ajax 实现。

1
2
3
4
5
$.ajax(url,{
dataType: 'json'
}).then(json =>{
// 进行操作
})

IE

如果需要在 IE 中使用 Promise,有两个选择:

Fetch API

Fetch API 是 XMLHttpRequest 的现代化替代方案。
可以在 service workers、Cache API 使用。

  • 更强大,更友好。
  • 直接返回一个 Promise 实例。
  • response 是一个二进制数据流,需要调用 json() 方法可以转换成 json 数据
1
2
3
4
5
6
7
fetch('some.json').then( res =>{
return res.json()
}).then( json =>{
// do something with the json
}).catch( err =>{
console.log(err);
})

async/await

ES2017 新增运算符,新的语言元素

  • 赋予 JavaScript 以顺序手法编写异步脚本的能力
  • 既保留异步运算的无阻塞特性,还继续使用同步写法。
  • 还能正常使用 return/try/catch。

async/await 仍然需要 Promise!

希望全面了解,请参考 async/await 入门

1
2
3
4
async function f1(){
const result = await resolveAfter2Seconds();
console.log(result);
}

协程

  1. 协程A开始执行。
  2. 协程A执行到一半,进入暂停,执行权转移到协程B。
  3. (一段时间后)协程B交还执行权。
  4. 协程A恢复执行。

Thunk

参数求值

传名调用,在执行时求值

1
2
3
4
5
6
7
8
9
10
11
12
13
function f(m){
return m * 2;
}

f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};

function f(thunk){
return thunk() * 2;
}

在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。

1
2
3
4
5
6
7
8
9
10
11
12
// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(单参数版本)
var readFileThunk = Thunk(fileName);
readFileThunk(callback);

var Thunk = function (fileName){
return function (callback){
return fs.readFile(fileName, callback);
};
};

任何函数,只要参数有回调函数,就能写成 Thunk 函数的形式。下面是一个简单的 Thunk 函数转换器。

1
2
3
4
5
6
7
8
9
10
11
12
13
var Thunk = function(fn){
return function (){
var args = Array.prototype.slice.call(arguments);
return function (callback){
args.push(callback);
return fn.apply(this, args);
}
};
};

// 使用上面的转换器,生成 fs.readFile 的 Thunk 函数。
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);

Thunkify 模块

生产环境的转换器,建议使用 Thunkify 模块

Thunk 函数的自动流程管理

控制执行权

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
29
30
31
32
33
34
var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

var gen = function* (){
var r1 = yield readFile('/etc/fstab');
console.log(r1.toString());
var r2 = yield readFile('/etc/shells');
console.log(r2.toString());
};


var g = gen();
var r1 = g.next();
r1.value(function(err, data){
if (err) throw err;
var r2 = g.next(data);
r2.value(function(err, data){
if (err) throw err;
g.next(data);
});
});


function run(fn) {
var gen = fn();
function next(err, data) {
var result = gen.next(data);
if (result.done) return;
result.value(next);
}
next();
}
run(gen);

Generator

Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function* gen(x){  // * 号区别
try {
var y = yield x + 2; // yield 暂停
} catch (e){
console.log(e);
}
return y;
}

var g = gen(1); // 返回的是指针对象
g.next() // { value: 3, done: false } value 属性是 yield 语句后面表达式的值
g.next() // { value: undefined, done: true } Generator 是否执行完毕
// 有参数2,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量 y 接收。
g.next(2) // { value: 2, done: true }
g.throw('出错了'// 捕获错误并抛出

相关资料
Promise 入门
Generator 函数的含义与用法
Thunk 函数的含义和用法
fetch polyfill兼容库
彻底搞懂JS事件中的循环机制 Event Loop
巨大的提升!重启的async函数和promises“译”

async/await的基础用法

Promise 初入门径
Promise 探讨
jQuery的deferred对象详解
Promises/A+ 规范


相关代码


开源
es6-promise

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.