某人

此前素未谋面、此后遥遥无期

0%

承诺Promises

Promises

Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的终值或本 promise 不能执行的原因。

一个 Promise有以下几种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
  1. Promise 本质是一个状态机。每个 promise 只能是 3 种状态中的一种:pending、fulfilledrejected。状态转变只能是 pending -> fulfilled 或者 pending -> rejected。状态转变不可逆。
  2. then 方法可以被同一个 promise 调用多次。
  3. then 方法必须返回一个 promise 。规范里没有明确说明返回一个新的 promise 还是复用老的 promise(即 return this),大多数实现都是返回一个新的 promise ,而且复用老的 promise 可能改变内部状态,这与规范也是相违背的。
  4. 值穿透(上一个promise传递过来的值,经由这个then方法的时候不做任何处理,而是交给再下个then方法去处理)。

注意: 如果一个promise对象处在fulfilledrejected状态而不是pending状态,那么它也可以被称为settled状态。你可能也会听到一个术语resolved ,它表示promise对象处于fulfilled状态。

术语集

  1. Promises:Promise规范自身
  2. promise对象:promise对象指的是 Promise 实例对象
  3. ES6 Promises:如果想明确表示使用 ECMAScript 6th Edition 的话,可以使用ES6作为前缀(prefix
  4. Promises/A+:这是ES6 Promises的前身,是一个社区规范,它和 ES6 Promises 有很多共通的内容。
  5. Thenable:类Promise对象。 拥有名为.then方法的对象。
  6. promise chain:指使用 then 或者 catch 方法将promise对象连接起来的行为。 此用语只是在本书中的说法,而不是在 ES6 Promises 中定义的官方用语。

Promise的实现类库

如果说一个类库兼容 Promises/A+ 的话,那么就是说它除了具有标准的 then 方法之外,很多情况下也说明此类库还支持 Promise.allcatch 等功能。

但是 Promises/A+ 实际上只是定义了关于 Promise#then 的规范,所以有些类库可能实现了其它诸如 allcatch 等功能,但是可能名字却不一样。

如果我们说一个类库具有 then 兼容性的话,实际上指的是 Thenable ,它通过使用 Promise.resolve 基于ES6 Promise的规定,进行promise对象的变换。

语法

1
new Promise( function(resolve, reject) {...} /* executor */  );

Promise 带有 resolvereject 两个参数的函数;

resolvereject 函数被调用时,分别将 promise 的状态改为fulfilled(完成)或 rejected(失败)

属性

  • Promise.length:length属性,其值总是为 1 (构造器参数的数目).
  • Promise.prototype:表示 Promise 构造器的原型.

方法

  • Promise.all(iterable):这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。

    这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;

    如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息

    Promise.all方法常被用于处理多个promise对象的状态集合

  • Promise.race(iterable):当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象

  • Promise.reject(reason):返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法

  • Promise.resolve(value):返回一个状态由给定value决定的Promise对象

    如果该值是一个Promise对象,则直接返回该对象;

    如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;

    否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法

    如果你不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该valuePromise对象形式使用。

Promise 原型

Promise.prototype.constructor

返回被创建的实例函数. 默认为 Promise 函数.

Promise.prototype.catch(onRejected)

添加一个拒绝(rejection) 回调到当前 promise, 返回一个新的 promise

当这个回调函数被调用,新 promise 将以它的返回值来resolve;

否则如果当前promise 进入fulfilled状态,则以当前promise的完成结果作为新promise的完成结果.

Promise.prototype.then(onFulfilled, onRejected)

添加解决(fulfillment)和拒绝(rejection)回调到当前 promise, 返回一个新的 promise, 将以回调的返回值来 resolve.

Promise.prototype.finally(onFinally)

添加一个事件处理回调于当前promise对象,并且在原promise对象解析完毕后,返回一个新的promise对象

回调会在当前promise运行完毕后被调用,无论当前promise的状态是完成(fulfilled)还是失败(rejected)

承诺源码

  1. 一个基础并高性能的承诺实现。
  2. immediate.js

代码片段1

此处的PromiseA等同于Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var promise = new PromiseA(function(resolve, reject) {
setTimeout(() =>{
console.log("开始测试")
resolve("1.小明")
},1000)
});
console.dir(promise, { depth: 10 })
promise.then((aVal) => {
return aVal+","+"2.小刚";
}).then((bVal) => {
return bVal+","+"3.小吃";
}).then((cVal) => {
console.log(cVal)
}).finally(function(){
console.log("结束测试")
})

结果:
image

then 方法可以被同一个 promise 调用多次

代码片段 allrace

1
2
3
4
5
6
7
8
9
10
11
12
13
var p1 = new PromiseA(function(resolve, reject) {
setTimeout(resolve, 500, '大哥');
});
var p2= new PromiseA(function(resolve, reject) {
setTimeout(resolve, 100, '小弟');
});
var p3= PromiseA.resolve("大爷");
PromiseA.all([p1,p2,p3]).then(function(pArr){
console.log("all",pArr)
})
PromiseA.race([p1,p2,p3]).then(function(p){
console.log("race",p)
})

结果:

1
2
race 大爷
all ["大哥", "小弟", "大爷"]0: "大哥"1: "小弟"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
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
'use strict';

/**
* immediate是一个微任务库,来自NobleJS的 setImmediate,延迟执行
* 把一些需要长时间运行的操作放在一个回调函数里、延迟执行
*/
var immediate = require('immediate');

/**
* 代码覆盖率工具 Istanbul,
* istanbul 提供注释语法,允许某些代码不计入覆盖率。
* 忽略覆盖 、空函数
*/
/* istanbul ignore next */
function INTERNAL() {}

/* 空对象 */
var handlers = {};

/**
* 定义 3 种状态
* [REJECTED 操作失败]
* [FULFILLED 操作成功]
* [PENDING 初始状态,既不是成功,也不是失败状态]
*/
var REJECTED = ['REJECTED'];
var FULFILLED = ['FULFILLED'];
var PENDING = ['PENDING'];

/* 忽略else路径 */
/* istanbul ignore else */
if (!process.browser) {
// in which we actually take advantage of JS scoping
var UNHANDLED = ['UNHANDLED'];
}

module.exports = Promise;

/**
* [Promise构造函数]
* @param {[Function]} resolver [函数]
* new Promise(function resolver(resolve, reject) {
* setTimeout(() => {
* resolve('haha')
* }, 1000)
* })
*/
function Promise(resolver) {
/* resolver必须是一个函数 */
if (typeof resolver !== 'function') {
throw new TypeError('resolver must be a function');
}
this.state = PENDING;//初始状态
this.queue = [];// promise 内部的回调队列
this.outcome = void 0;
/* 忽略if路径 */
/* istanbul ignore else */
if (!process.browser) {
this.handled = UNHANDLED;
}
/**
* Promise 构造函数传入了一个 INTERNAL 即空函数,因为这个新产生的 promise可以认为是内部的 promise;
* 需要根据外部的 promise 的状态和值产生自身的状态和值,不需要传入回调函数,
* 而外部Promise 需要传入回调函数决定它的状态和值。所以之前 Promise 的构造函数里做了判断区分外部
* 调用还是内部调用
*/
if (resolver !== INTERNAL) {
/* 安全的执行 then 函数 */
safelyResolveThenable(this, resolver);
}
}

/**
* finally() 方法返回一个Promise,在执行then()和catch()后,都会执行finally指定的回调函数。避免同样的语句需要在then()和catch()中各写一次的情况。
* @param {Function} callback [description]
* @return {[type]} [返回一个Promise]
*/
Promise.prototype.finally = function(callback) {
if (typeof callback !== 'function') {
return this;
}
var p = this.constructor;
return this.then(resolve, reject);

function resolve(value) {
function yes() {
return value;
}
return p.resolve(callback()).then(yes);
}

function reject(reason) {
function no() {
throw reason;
}
return p.resolve(callback()).then(no);
}
};
/**
* catch() 方法返回一个Promise,并且处理拒绝的情况
* @param {[type]} onRejected [失败操作]
* @return {[type]} [返回一个Promise]
*/
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};

/**
* [then 原型方法]
* @param {[type]} onFulfilled [then的 成功回调]
* @param {[type]} onRejected [then的 失败回调]
* 生成了一个新的 promise 然后返回,符合规范要求
*/
Promise.prototype.then = function(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
typeof onRejected !== 'function' && this.state === REJECTED) {
return this;//实现了值穿透
}
/**
* Promise 构造函数传入了一个 INTERNAL 即空函数,因为这个新产生的 promise 可以认为是内部的
* promise;需要根据外部的 promise 的状态和值产生自身的状态和值,不需要传入回调函数,
* 而外部Promise 需要传入回调函数决定它的状态和值;所以之前 Promise 的构造函数里做了判断区分外部
* 调用还是内部调用
*/
var promise = new this.constructor(INTERNAL);
/* 覆盖测试忽略else路径 */
/* istanbul ignore else */
if (!process.browser) {
if (this.handled === UNHANDLED) {
this.handled = null;
}
}
if (this.state !== PENDING) {
//state状态改变、根据状态返回成功或失败回调、则调用 unwrap
var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
//传入内部承诺、
unwrap(promise, resolver, this.outcome);
} else {
//将生成的 promise 加入到当前 promise 的队列 queue 里
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
}

return promise;
};

/**
* [QueueItem 承诺队列]
* @param {[type]} promise [promise 为 then 生成的新 promise/子承诺]
* @param {[type]} onFulfilled [then 参数中的 onFulfilled]
* @param {[type]} onRejected [then 参数中的 onRejected]
*/
function QueueItem(promise, onFulfilled, onRejected) {
this.promise = promise;
if (typeof onFulfilled === 'function') {
this.onFulfilled = onFulfilled;
this.callFulfilled = this.otherCallFulfilled;
}
if (typeof onRejected === 'function') {
this.onRejected = onRejected;
this.callRejected = this.otherCallRejected;
}
}
//承诺成功调用
QueueItem.prototype.callFulfilled = function(value) {
handlers.resolve(this.promise, value);
};
//用 otherCallFulfilled 调用 unwrap 进行解包最终得出子 promise 的状态和值
QueueItem.prototype.otherCallFulfilled = function(value) {
unwrap(this.promise, this.onFulfilled, value);
};
//承诺失败调用
QueueItem.prototype.callRejected = function(value) {
handlers.reject(this.promise, value);
};
//用 otherCallRejected 调用 unwrap 进行解包最终得出子 promise 的状态和值
QueueItem.prototype.otherCallRejected = function(value) {
unwrap(this.promise, this.onRejected, value);
};

/**
* [unwrap 解包函数(即执行函数)、]
* @param {[type]} promise [子 promise/内部承诺]
* @param {[type]} func [父 promise 的 then 的回调(onFulfilled/onRejected)]
* @param {[type]} value [父 promise 的值(正常值/错误)]
* immediate 是 立即激活回调,然后等待后续事件在一定时间内触发。
*/
function unwrap(promise, func, value) {
//把一些需要长时间运行的操作放在一个回调函数里、延迟执行、将代码装换成异步代码
immediate(function() {
var returnValue;
//捕获 then/catch 内抛出的异常,并调用 handlers.reject
try {
//执行父 promise 的 then 的回调(onFulfilled/onRejected),并传入父 promise 的值(正常值/错误)
returnValue = func(value);
} catch (e) {
return handlers.reject(promise, e);
}
//返回的值不能是 内部promise 本身,否则会造成死循环
if (returnValue === promise) {
//调用拒绝
handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
} else {
//调用拒成功
handlers.resolve(promise, returnValue);
}
});
}

/**
* [resolve description]
* @param {[type]} self [promise]
* @param {[type]} value [父承诺的值]
* @return {[type]} [返回自身]
*/
handlers.resolve = function(self, value) {
var result = tryCatch(getThen, value);
//有错误则执行 handlers.reject
if (result.status === 'error') {
return handlers.reject(self, result.value);
}
var thenable = result.value;
//如果返回值存在
if (thenable) {
/* 安全的执行 then 函数 */
safelyResolveThenable(self, thenable);
} else {
self.state = FULFILLED;
self.outcome = value;
var i = -1;
var len = self.queue.length;
while (++i < len) {
self.queue[i].callFulfilled(value);
}
}
return self;
};

//操作失败
handlers.reject = function(self, error) {
self.state = REJECTED;
self.outcome = error;
/* 覆盖测试忽略else路径 */
/* istanbul ignore else */
if (!process.browser) {
if (self.handled === UNHANDLED) {
immediate(function() {
if (self.handled === UNHANDLED) {
process.emit('unhandledRejection', error, self);
}
});
}
}
var i = -1;
var len = self.queue.length;
while (++i < len) {
self.queue[i].callRejected(error);
}
return self;
};

/**
* [getThen 辅助函数 getThen]
* @param {[type]} obj [值或承诺]
* @return {[type]} [description]
* 规范中规定:如果 then 是函数,这里是 obj作为函数的 this 调用
*/
function getThen(obj) {
// Make sure we only access the accessor once as required by the spec
var then = obj && obj.then;
//只有obj是一个对象或函数并且含有的属性then是函数,如obj是一个承诺
if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
//返回解包函数类似tryToUnwrap
return function appyThen() {
//执行then方法,这里 obj 作为函数的 this 调用。
then.apply(obj, arguments);
};
}
}

/**
* [safelyResolveThenable 安全的执行 then 函数]
* 构造函数的参数resolver,即这里的 thenable
* new Promise(function resolver(resolve, reject) {
* setTimeout(() => {
* resolve('haha')
* }, 1000)
* })
*/
function safelyResolveThenable(self, thenable) {
// Either fulfill, reject or reject with error
var called = false;

/* 失败调用 */
function onError(value) {
if (called) {
return;
}
called = true;
handlers.reject(self, value);
}

/* 成功调用 */
function onSuccess(value) {
if (called) {
return;
}
called = true;
handlers.resolve(self, value);
}

//捕获并解包函数
function tryToUnwrap() {
//执行传进来的函数
thenable(onSuccess, onError);
}

/**
* 执行并返回结果
*/
var result = tryCatch(tryToUnwrap);
//如果抛出异常、则调用onError里的handlers.reject
if (result.status === 'error') {
onError(result.value);
}
}

/**
* [tryCatch 捕获抛出的异常]
* @param {[Function]} func [then函数]
* @param {[type]} value [父函数then返回的值]
* @return {[Object]} [返回执行结果]
*/
function tryCatch(func, value) {
var out = {};
try {
//执行then函数,传入父then返回的值
out.value = func(value);
out.status = 'success';
} catch (e) {
out.status = 'error';
out.value = e;
}
return out;
}

//静态方法成功
Promise.resolve = resolve;

function resolve(value) {
//Promise.resolve 参数是一个 promise 时,直接返回该值
if (value instanceof this) {
return value;
}
//传入新的承诺
return handlers.resolve(new this(INTERNAL), value);
}

//静态方法失败
Promise.reject = reject;

function reject(reason) {
//传入新的承诺
var promise = new this(INTERNAL);
return handlers.reject(promise, reason);
}

Promise.all = all;

/**
* [此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。]
* @param {[Array]} iterable [description]
* @return {[type]} [返回一个 Promise 实例]
*/
function all(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
return this.reject(new TypeError('must be an array'));
}

var len = iterable.length;
//called 用来控制即使有多个 promise reject 也只有第一个生效
var called = false;
if (!len) {
return this.resolve([]);
}

//values 用来存储结果
var values = new Array(len);
var resolved = 0;
var i = -1;
//生成新承诺
var promise = new this(INTERNAL);

//循环处理所有的
while (++i < len) {
allResolver(iterable[i], i);
}
return promise;

function allResolver(value, i) {
self.resolve(value).then(resolveFromAll, function(error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});

function resolveFromAll(outValue) {
values[i] = outValue;
if (++resolved === len && !called) {
called = true;
handlers.resolve(promise, values);
}
}
}
}

Promise.race = race;

/**
* Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
* @param {[Array]} iterable [description]
* @return {[type]} [description]
*/
function race(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
return this.reject(new TypeError('must be an array'));
}

var len = iterable.length;
/* called 控制只要有任何一个 promise onFulfilled/onRejected 立即去设置 promise 的状态和值。*/
var called = false;
if (!len) {
return this.resolve([]);
}

var i = -1;
//生成一个新的承诺
var promise = new this(INTERNAL);

while (++i < len) {
resolver(iterable[i]);
}
return promise;

function resolver(value) {
self.resolve(value).then(function(response) {
if (!called) {
called = true;
handlers.resolve(promise, response);
}
}, function(error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});
}
}

参考注释

  • 平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilledonRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用“宏任务(macro-task)”机制或者“微任务(micro-task)”机制来实现。由于 promise 的实施代码本身就是平台代码(译者注:即都是 JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。

macrotaskmicrotask 两个概念,这表示异步任务的两种分类。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

两个类别的具体分类如下:

  1. macro-task: script(整体代码),setTimeout, setInterval, setImmediate,I/O, UI rendering
  2. micro-task: process.nextTick, Promises(这里指浏览器实现的原生 Promise), Object.observe, MutationObserver

相关链接

  1. Promise/A 规范
  2. 深入 Promise(一)——Promise 实现详解
  3. Promises Book
  4. 翻译Promises/A+规范