Promise 基础

1
2
3
4
5
6
7
8
9
10
11
// 回调地狱: 在需要多个操作的时候, 会导致多个回调函数嵌套, 导致代码不够直观
// 并行结果: 若几个异步操作之间并没有前后顺序之分, 但需要等多个异步操作都完成后才能执行后续任务, 无法实现并行节约时间
$.ajax('p1', success() {
$.ajax('p2', success() {
$.ajax('p3', success() {
$.ajax('p4', success() {

})
})
})
})

Promise 是一个类, 可以创建实例, 代表承诺, 一般用于异步任务, 解决需要很长时间执行的问题

三种状态, 不可逆

  1. pending 初始态
  2. fulfilled 成功态
  3. rejected 失败态

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
let p1 = new Promise(function (resolve, reject) {
// throw Error('异常');
setTimeout(function () {
let num = Math.random();
if (num > .5) {
// 如果这个 promise 成功了, 会调成功的回调 fuifilled
resolve(100);
} else {
// 如果这个 promise 失败了, 会调失败的回调 rejected
reject(100);
}
}, 1000);
})
// 成功回调后的值被用来 resolve 当前的 promise
// 成功的回调里又返回了一个新的 **promise**
let p2 = p1.then(function (data) {
console.log('success1: ', data);
return data + 100;
}, function (error) {
console.log('failed1: ', error);
return error + 100;
})
p2.then(function (data) {
console.log('success2: ', data);
}, function (error) {
console.log('failed2: ', error);
})
let p3 = p1.then(function (data) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(data + 1000);
})
}))
}, 1000)
})
})
p3.then(function (data) {
console.log('success3: ', data);
}, function (error) {
console.log('failed3: ', error);
})

Promise 简单实现

参考promise-A-Plus 规范

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
// 构造函数的参数是一个异步任务
function Promise(task) {
// 缓存 this
let _this = this;
// 默认状态为 pending
_this.status = 'pending';
// 该变量存放 promise 的结果
_this.value = undefined;
// 存放所有的成功/失败的回调函数
_this.onResolvedCallbacks = [];
_this.onRejectedCallbacks = [];
// 调用此方法可将当前 promise 变为成功态
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
// 如果当前状态是初始态, 则转成成功态
if (_this.status === 'pending') {
_this.status = 'fulfilled';
_this.value = value;
_this.onResolvedCallbacks.forEach(item => item(_this.value));
}
}
// 调用此方法可将当前 promise 变为失败态
function reject(reason) {
// 如果当前状态是初始态, 则转成失败态
if (_this.status === 'pending') {
_this.status = 'rejected';
_this.value = reason;
_this.onRejectedCallbacks.forEach(item => item(_this.value));
}
}
// 立即执行传入的任务, 捕获异常
try {
task(resolve, reject);
} catch (e) {
reject(e);
}
}
function resolvePromise(_promise, x, resolve, reject) {
let then;
// 如果 x 就是 _promise
if (_promise === x) {
return reject(new TypeError('循环引用'));
}
if (x instanceof Promise) {
if (x.status === 'pending') {
x.then(function (y) {
resolvePromise(_promise, y, resolve, reject)
}, reject)
} else if (x.status === 'fulfilled') {
resolve(x.value);
} else if (x.status === 'rejected') {
reject(x.value);
}
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
then = x.then;
if (typeof then === 'function') {
then.call(x, function (y) {
resolvePromise(_promise, y, resolve, reject);
}, reject)
}
} catch (e) {
reject(e);
}
} else {
resolve(x);
}
}
// onFulfilled 成功的回调, onRejected 失败的回调
Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function'
? onFulfilled
: function (value) { return value }
onRejected = typeof onRejected === 'function'
? onRejected
: function (reason) { throw reason }
let _this = this;
let _promise;
if (_this.status === 'pending') {
_promise = new Promise(function (resolve, reject) {
_this.onResolvedCallbacks.push(function () {
let x = onFulfilled(_this.value);
resolvePromise(_promise, x, resolve, reject);
});
_this.onRejectedCallbacks.push(function () {
let x = onRejected(_this.value);
resolvePromise(_promise, x, resolve, reject);
});
})
}
if (_this.status === 'fulfilled') {
_promise = new Promise(function (resolve, reject) {
let x = onFulfilled(_this.value);
resolvePromise(_promise, x, resolve, reject);
})
}
if (_this.status === 'rejected') {
_promise = new Promise(function (resolve, reject) {
let x = onRejected(_this.value);
resolvePromise(_promise, x, resolve, reject);
})
}
return _promise;
}

异步

  • 异步: 一段任务分为两段, 先执行第一段, 然后转而执行其它任务, 等做好准备后, 再回头执行第二段(不限续执行)
  • 同步: 连续的执行

高阶函数

在 JavaScript 中, 函数式一等公民, 可以作为函数的返回值, 也可以作为函数的参数

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
// 像以下的判断需要写很多函数, 这时就需要高阶函数
function isString(param) {
return Object.prototype.toString.call(param) === '[object String]';
}
function isArray(param) {
return Object.prototype.toString.call(param) === '[object Array]';
}
isString({});
isArray([]);

/**
* 高阶函数
* 可以作为函数的返回值, 也可以作为函数的参数
*/
// 1. 函数可以作为返回值
function isType(type) {
return function (param) {
return Object.prototype.toString.call(param) === `[object ${type}]`;
}
}
let isString = isType('String')({});
let isArray = isType('Array');
isArray([]);
console.log(isStirng, isArray);
// 2. 函数可以作为参数传到另一个函数内
// e.g: lodash after 指定一个函数被调用多少次才会真正执行
function say() { console.log('we\'re done') }
function after(times, fn) {
let count = 0;
return function () {
if (++count === times) { fn() }
}
}
let realSay = after(3, say);
realSay(); // 不执行
realSay(); // 不执行
realSay(); // 执行

异步编程目标

语法目标: 就是怎样然它更像同步编程, 如

  • 回调函数实现
  • 事件监听
  • 发布订阅
  • PromiseA+ 和 Generator(生成器) 函数
  • async/await

回调函数

回调函数, 就是把任务的第二段单独写在一个函数中, 等到重新执行这个任务的时候, 就直接调用这个函数

回调的特点是 error first, 调用回调函数的时候第一个参数永远是错误对象

1
2
3
4
5
6
7
8
9
10
11
12
13
// e.g 异步读取文件
let fs = require('fs');
function readSync(filename) {
fs.readFile(filename, 'utf8', function (err, data) {
if (error) {
console.log(error);
} else {
console.log(data);
}
})
}
let result = readSync('./readme.txt');
console.log(1); // 因为上面是异步代码, 会先执行该代码

回调函数的问题: 异步导致

  1. 无法捕获错误(不能使用 try/catch)
    1
    2
    3
    4
    5
    try {
    read('./readme.txt'); // 捕获不到错误, 因为异步
    } catch (e) {
    console.log('err', e);
    }
  2. 无法 return
    1
    let data = read('./readme.txt');
  3. 回调地狱
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 当访问服务器时, 如请求一个 HTML 页面, 比如是用户列表; 服务器一方面会去读取模版文件, 如 ejs pug jade handlebar, 另一方面还要读取数据(可能回到在文件/数据中), 它们都很慢, 所以在 node 中会是异步的
    // 这种恶魔金字塔有以下问题
    // 1. 难以阅读
    // 2. 难以维护
    // 3. 效率比较低, 因为是串行的
    let fs = require('fs');
    fs.readFile('./template.txt', 'utf8', function (err, temp) {
    fs.readFile('./data.txt', 'utf8', function (err, data) {
    fs.readFile('./n.txt', 'utf8', function (err, data) {
    // ...
    })
    })
    })

**深度克隆简单实现**

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
let obj = {
age: 5,
getAge() {
console.log(this.age);
},
hobby: [1, 2, 3],
home: { city: 'HangZhou' }
}
let obj2 = deepClone(obj);
function deepClone(parent, child) {
child = child ? child : [];
for (let key in parent) {
if (parent.hasOwnProperty(key) {
if (typeof parent[key] == 'object') {
child[key] = Object.prototype.toString.call(parent[key]) == '[object Object]'
? {}
: []
deepClone(parent[key], child[key]);
} else {
child[key] = parent[key];
}
})
}
return child;
}

异步流程解决方案

事件发布/订阅模型

node 核心模块中的一个类, 通过它可以创建事件发射器的实例, 里面有两个核心方法: on & emit, on 表示注册监听, emit 表示发射事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const EventEmitter = require('events');
const eve = new EventEmitter();
// 用来存放最终数据
let html = {};
// 监听数据获取成功事件, 当事件发生后调用回调函数
eve.on('ready', function (key, value) {
html[key] = value;
if (Object.keys(html).length === 2) {
eve.off('ready');
console.log(html);
}
})
fs.readFile('./template.txt', 'utf8', function (err, template) {
// 1. 事件名; 2. 属性名; 3. 属性值;
// 2 参数往后是传递给回调函数的参数
eve.emit('ready', 'template', template);
})
fs.readFile('./data.txt', 'utf8', function (err, data) {
eve.emit('ready', 'data', data);
})

哨兵变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function render(length, cb) {
let html = {};
return function (key, value) {
html[key] = value;
if (Object.keys(html).length === length) {
cb(html);
}
}
}
let finished = render(2, function (content) {
console.log(content);
})
fs.readFile('./template.txt', 'utf8', function (err, template) {
finished('template', template);
})
fs.readFile('./data.txt', 'utf8', function (err, data) {
finished('data', data);
})

生成器 Generator/yield

  • 当执行一个函数时, 可以在某个点暂停函数的执行, 并且做一些其他工作, 然后再返回这个函数继续执行, 甚至是携带一些新的值, 然后继续执行
  • 以上场景正是 JS 生成器函数致力解决的问题: 当调用一个生成器函数时, 并不会立即执行, 而是需要手动去执行迭代操作(next 方法), 也就是说调用生成器函数, 它会返回给你一个迭代器, 迭代器会遍历每一个中断点
  • next 方法返回值的 value 属性, 是 Generator 函数向外输出的数据; next 方法还可以接受参数, 这是向 Generator 函数体内输入数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 生成器函数有一个特点, 需要加个 *
// 生成器函数有若干个阶段, 依靠 yield 划分
function *gen('这是 a 的值') {
console.log(1);
// 注: 此处的 b 用来供外界(next)输入值进来的
// 这一行实现输入和输出, 本次的输出放在 yield 后面, 下次的输入放在 yield 前面
let b = yield a;
console.log(2);
let c = yield b;
console.log(3);
return c;
}
// 调用生成器函数并不会立即执行
const it = gen('is a value');
// 它会返回此生成器的迭代器, 迭代器是一个对象, 每调用一次 next 就可以返回一个值对象,
// 调用 next 会返回一个对象, 此对象有两个属性:
// value 就是 yield 后面的那个值
// done 表示是否迭代完成

// 注: next 第一次不需要传参, 传参数没有意义
const r1 = it.next(); // { value: 'is a value', done: false }
const r2 = it.next('这才是 b 的值'); // { value: '这才是 b 的值', done: false }
const r3 = it.next(); // { value: undefined, done: true }
const r3 = it.next('is c') // { value: 'is c', done: true }

next 传参就是给上一个 yield 表达式赋值, yield 表达式本身没有返回值, 它只是一个输出标识符

生成器 + Promise + co

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
let fs = require('fs');
function readFile(filename) {
return new Promise(function (resolve, reject) {
fs.readFile(filename, 'utf8', function (err, data) {
err ? reject(err) : resolve(data);
})
})
}
function *read() {
let a = yield readFile('1.txt');
console.log(a);
let b = yield readFile('2.txt');
console.log(b);
return b;
}
// 调用生成器, 返回迭代器
let it = read();
let r1 = it.next(); // { value: promise1, done: false }
r1.value.then(function (data) {
let r2 = it.next(data); // { value: promise2, done: false }
r2.value.then(function (data) {
let r3 = it.next(data);
console.log(r3, 'is r3'); // { value: '', done: true }
})
})

co 是用来帮我们自动执行迭代器的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// let co = require('co');
function co(generator) {
// 要让生成器持续执行
let it = generator();
return new Promise(function (resolve, reject) {
!function next(lastVal) {
let { value, done } = it.next(lastVal);
if (done) {
resolve(value);
} else {
value.then(next, reject);
}
}()
});
}
co(read).then(function (data) {
console.log(data);
});

async/await

当前的异步终极解决方案, 是最简单的, 但它其实只是 generator + promise 的语法糖

await 后面必须跟一个 promise

优势

  1. 简洁
  2. 语意化
  3. 可以很好的处理异步 throw error、return、try/catch

目前 koa2 中已经可以支持 async/await

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
let fs = require('fs');
function readFile(filename) {
return new Promise(function (resolve, reject) {
fs.readFile(filename, 'utf8', function (err, data) {
err ? reject(err) : resovle(data);
})
})
}
async function read() {
let a = await readFile('./1.txt');
console.log(a);
try {
let b = await readFile('./2.txt'); // 如果有异常可以捕获
} catch(e) {
console.error(e);
}
console.log(b);
let c = await readFile('./3.txt');
console.log(c);
return 'ok';
}
read().then(data => {
console.log(data); // 'ok'
})
/*
* async/await 是语法糖, 内部还是用 generator + promise 实现
* 上述 read 方法经过转译后应该为
*/
let co = require('co');
function read() {
return co(function *() {
let a = yield readFile('./1.txt');
console.log(a);
let b = yield readFile('./2.txt');
console.log(b);
let c = yield readFile('./3.txt');
console.log(c);
return 'ok';
})
}

再探 Promise

这版是 Promise 完整实现, 包括测试用例, 以下是测试结果
result

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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function Promise(executor) {
// 先缓存当前 promise 实例
const _this = this;
// 设置状态
_this.status = PENDING;
// 定义存放成功/失败的回调
_this.onResolvedCallbacks = [];
_this.onRejectedCallbacks = [];

/*
* 规范 2.1
* 当调用该方法时, 若 promise 状态为 pending 可以转成成功态; 但若已经是成功/失败态, 则什么都不做
*/
// 规范 2.1.1
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
setTimeout(function () {
// 如果是初始态, 则转为成功态
if (_this.status == PENDING) {
_this.status = FULFILLED;
// 成功后会得到一个值, 这个值不能修改
_this.value = value;
// 调用所有成功的回调函数
_this.onResolvedCallbacks.forEach(cb => cb(_this.value));
}
})
}
// 规范 2.1.2
function reject(reason) {
setTimeout(function () {
// 如果是初始态, 则转为失败态
if (_this.status == PENDING) {
_this.status = REJECTED;
_this.value = reason;
_this.onRejectedCallbacks.forEach(cb => cb(_this.value));
}
})
}
try {
// 因为该函数执行可能会异常, 所以需要捕获
executor(resolve, reject);
// 如果出错了, 需要用错误对象 reject
} catch (e) {
// 如果函数执行失败, 则用失败的原因 reject 这个 promise
reject(e);
}
}
/**
* 规范 2.3
*/
function resolvePromise(_promise, x, resolve, reject) {
// 规范 2.3.1: 适用于以下情况: 返回了自己/等待自己完成, 永远无法执行完毕, 但是不会死循环
// let p2 = p1.then((data) => p2)
if (_promise === x) {
return reject(new TypeError('循环引用'));
}
// _promise 是否已经 resolve/reject
let called = false;
// 规范 2.3.2: 如果 x 是一个 promise, 使用它的状态
if (x instanceof Promise) {
if (x.status == PENDING) {
x.then(function (y) {
resolvePromise(_promise, y, resolve, reject);
}, reject)
} else {
x.then(resolve, reject);
}
} else if (x != null && ((typeof x == 'object') || (typeof x == 'function'))) {
// 规范 2.3.3: 如果 x 是一个 thenable 对象或函数, 只要有 then 方法的对象
// 让自己的 promise 和别的 promise 进行交互, 编写这段代码时尽量考虑兼容性, 允许别人瞎写
// 有些 promise 会同时执行成功和失败的回调
try {
let then = x.then;
if (typeof then == 'function') {
then.call(x, function (y) {
// 如果 _promise 已经成功/失败了, 则不会再处理了
if (called) return;
called = true;
resolvePromise(_promise, y, resolve, reject);
}, function (err) {
if (called) return;
called = true;
reject(err);
});
} else {
// 到这里的话说明 x 不是一个 thenable 对象, 直接将它当成值 resolve _promise 即可
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 如果 x 是一个普通的值, 则用 x 的值去 resolve _promise
resolve(x);
}
}
/**
* 规范 2.2
* onFulfilled: 用来接收 promise 成功的值
* onRejected: 用来接收 promise 失败的原因
*/
Promise.prototype.then = function (onFulfilled, onRejected) {
// 规范 2.2.1: 如果成功和失败的回调函数没有传, 则表示这个 then 没有任何逻辑, 只会把值往后抛
onFulfilled = typeof onFulfilled == 'function'
? onFulfilled
: value => value;
onRejected = typeof onRejected == 'function'
? onRejected
: reason => { throw reason };
// 如果当前 promise 状态已经是成功态了, 则使用 onFulfilled 直接取值
let _this = this;
let _promise;
if (_this.status == FULFILLED) {
return _promise = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onFulfilled(_this.value);
// 如果获取到了返回值 x, 会走解析 promise 的过程
resolvePromise(_promise, x, resolve, reject);
} catch(e) {
// 如果执行成功的回调函数中出错了, 用错误原因把 _promise reject
reject(e);
}
})
})
}
// 如果当前 promise 状态已经是失败态了, 则使用 onRejected 直接取值
if (_this.status == REJECTED) {
return _promise = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRejected(_this.value);
resolvePromise(_promise, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
}
if (_this.status == PENDING) {
return _promise = new Promise(function (resolve, reject) {
_this.onResolvedCallbacks.push(function () {
try {
let x = onFulfilled(_this.value);
resolvePromise(_promise, x, resolve, reject);
} catch(e) {
reject(e);
}
})
_this.onRejectedCallbacks.push(function () {
try {
let x = onRejected(_this.value);
resolvePromise(_promise, x, resolve, reject);
} catch(e) {
reject(e);
}
})
})
}
}
// catch 原理就是只传失败的回调
Promise.prototype.catch = function (onRejected) {
this.then(null, onRejected);
}
// all 接收一个 promise 数组, 如果 promise 全部完成, 这个 promise 才会成功, 如果有一个失败则整个失败
// 同时异步请求多个数据时
// Promise.all([p1, p2])
function gen(times, cb) {
let result = [], count = 0;
return function (i, data) {
result[i] = data;
if (++count == times) {
cb(result);
}
}
}
Promise.prototype.all = function (promises) {
return new Promise(function (resolve, reject) {
let done = gen(promises.length, resolve);
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
done(i, data);
});
}
}, reject)
}
// race 接收一个 promise 数组, 只要有一个成功就成功
// 当有多个接口都不稳定, 可以同时取三个接口, 谁先回来用谁的
// Promise.race([p1, p2])
Promise.prototype.race = function (promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
})
}
// 用于测试用例
Promise.deferred = Promise.defer = function () {
let defer = {};
defer.promise = new Promise(function (resolve, reject) {
defer.resolve = resolve;
defer.reject = reject;
})
return defer;
}
// 因为 onFulfilled/onRejected 是第三方提供的方法, 这就意味着代码可能同步也可能异步,
// 为了保障代码的稳定性, 通过以下两种方法, 来将普通值转化成一个 promise 对象
// 返回一个立即成功的 promise
Promise.resolve = function (value) {
return new Promise(function (resolve) {
resolve(value);
})
}
// 返回一个立即失败的 promise
Promise.reject = function (reason) {
return new Promise(function (resolve) {
resolve(reason);
})
}

module.exports = Promise;

Promise 其它实现方式

NodeJS q 模块的实现

q 是 promise 其中的一种实现方式, 在 angular.js 中 promise 就是用的 q

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
// let Q = require('q');
let Q = {
defer() {
let success, error;
return {
resolve(data) {
success(data);
},
reject(err) {
error(err);
},
promise: {
then(onFulfilled, onRejected) {
success = onFulfilled;
error = onRejected;
}
}
}
}
}
// 调用
let fs = require('fs');
function readFile(filename) {
let defer = Q.defer();
fs.readFile(filename, 'utf8', function (err, data) {
if (err) {
defer.reject(err);
} else {
defer.resolve(data);
}
})
return defer.promise;
}
readFile('./1.txt').then(function (data) {
console.log(data);
})
// Q.spread 是扩展的意思
let r = Q.spread([Promise.resolve(1), Promise.resolve(2)], function (a, b) {
return a + b;
})

bluebird

bluebird 是世界上最快的 promise 库, 能把任意的通过回调函数实现的异步 API 转换成 promise API

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
let Promise = require('bluebird');
let readFile = require('fs').readFile;
// 会返回一个新的函数
let readFileSync = Promise.promisify(readFile);
readFileSync('./1.txt', 'utf8').then(function (data) {
console.log(data);
});

// promisify: 可以把一个普通的异步方法转成 promise 的方法
// 传递时应该是三个参数 filename/utf8/callback,
// 因为调用时只传了两个, 所以需要模拟出来 callback 将其放到里面去
function promisify(fn) {
return function (..args) {
return new Promise(function (resolve, reject) {
fn.apply(null, [...args, function (err, data) {
err ? reject(err) : resolve(data);
}])
})
}
}
// promisifyAll: 会便利对象上所有方法, 然后对每个方法添加一个新的方法 - Async
function promisifyAll(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key) && typeof obj[key] == 'function') {
obj[key + 'Async'] = Promise.promisify(obj[key]);
}
}
}