1. Promise/A+规范

2.Promise基础

空函数执行

  1. let p = new Promise(() => {});
  2. setTimeout(console.log, 0, p); // Promise <pending>

2.1. Promise状态

期约的状态是私有的,不能直接通过 JavaScript 检测到。

  • 待定(pending)
  • 兑现(fulfilled)
  • 拒绝(rejected)

Promise一旦fulfilled或者rejected就不能逆转

2.2. 解决值、拒绝理由及期约用例

2.3. 通过执行函数控制期约状态

由于期约的状态是私有的,所以只能在内部进行操作。内部操作在期约的执行器函数中完成。执行器函数主要有两项职责:初始化期约的异步行为和控制状态的最终转换。其中,控制期约状态的转换是通过调用它的两个函数参数实现的。这两个函数参数通常都命名为resolve()和 reject()。调用resolve()会把状态切换为兑现,调用 reject()会把状态切换为拒绝。另外,调用 reject()也会抛出错误(后面会讨论这个错误)。

  1. let p1 = new Promise((resolve, reject) => resolve());
  2. setTimeout(console.log, 0, p1); // Promise <resolved>
  3. let p2 = new Promise((resolve, reject) => reject());
  4. setTimeout(console.log, 0, p2); // Promise <rejected>
  5. // Uncaught error (in promise)

在前面的例子中,并没有什么异步操作,因为在初始化期约时,执行器函数已经改变了每个期约的
状态。这里的关键在于,执行器函数是同步执行的。这是因为执行器函数是期约的初始化程序。通过下
面的例子可以看出上面代码的执行顺序:

  1. new Promise(() => setTimeout(console.log, 0, 'executor'));
  2. setTimeout(console.log, 0, 'promise initialized');
  3. // executor
  4. // promise initialized
  1. let p = new Promise((resolve, reject) => setTimeout(resolve, 1000));
  2. // When this console.log executes, the timeout callback has not yet executed:
  3. setTimeout(console.log, 0, p); // Promise <pending>

example4

  1. let p = new Promise((resolve, reject) => {
  2. resolve();
  3. reject(); // No effect
  4. });
  5. setTimeout(console.log, 0, p); // Promise <resolved>
  1. let p = new Promise((resolve, reject) => {
  2. setTimeout(reject, 10000); // After 10 seconds, invoke reject()
  3. // Do executor things
  4. });
  5. setTimeout(console.log, 0, p); // Promise <pending>
  6. setTimeout(console.log, 11000, p); // Check state after 11 seconds
  7. // (After 10 seconds) Uncaught error
  8. // (After 11 seconds) Promise <rejected>

2.4 Promise.resolve()

期约并非一开始就必须处于待定状态,然后通过执行器函数才能转换为落定状态。通过调用
Promise.resolve()静态方法,可以实例化一个解决的期约。下面两个期约实例实际上是一样的:

  1. let p1 = new Promise((resolve, reject) => resolve());
  2. let p2 = Promise.resolve();

这个解决的期约的值对应着传给 Promise.resolve()的第一个参数。使用这个静态方法,实际上
可以把任何值都转换为一个期约:

  1. setTimeout(console.log, 0, Promise.resolve());
  2. // Promise <resolved>: undefined
  3. setTimeout(console.log, 0, Promise.resolve(3));
  4. // Promise <resolved>: 3
  5. // Additional arguments are ignored
  6. setTimeout(console.log, 0, Promise.resolve(4, 5, 6));
  7. // Promise <resolved>: 4

image.png

  1. let p = Promise.resolve(7);
  2. setTimeout(console.log, 0, p === Promise.resolve(p));
  3. // true
  4. setTimeout(console.log, 0, p === Promise.resolve(Promise.resolve(p)));
  5. // true
  1. let p = new Promise(() => {});
  2. setTimeout(console.log, 0, p); // Promise <pending>
  3. setTimeout(console.log, 0, Promise.resolve(p)); // Promise <pending>
  4. setTimeout(console.log, 0, p === Promise.resolve(p)); // true

2.5 Promise.reject()

与 Promise.resolve()类似,Promise.reject()会实例化一个拒绝的期约并抛出一个异步错误
(这个错误不能通过 try/catch 捕获,而只能通过拒绝处理程序捕获)。下面的两个期约实例实际上是
一样的:

  1. let p1 = new Promise((resolve, reject) => reject());
  2. let p2 = Promise.reject();
  1. let p = Promise.reject(3);
  2. setTimeout(console.log, 0, p); // Promise <rejected>: 3
  3. p.then(null, (e) => setTimeout(console.log, 0, e)); // 3

2.6 同步/异步执行的二元性

  1. try {
  2. throw new Error('foo');
  3. } catch(e) {
  4. console.log(e); // Error: foo
  5. }
  6. try {
  7. Promise.reject(new Error('bar'));
  8. } catch(e) {
  9. console.log(e);
  10. }
  11. // Uncaught (in promise) Error: bar
  12. // ExecutionDualityExample01.js

第一个 try/catch 抛出并捕获了错误,第二个 try/catch 抛出错误却没有捕获到。乍一看这可能
有点违反直觉,因为代码中确实是同步创建了一个拒绝的期约实例,而这个实例也抛出了包含拒绝理由
的错误。这里的同步代码之所以没有捕获期约抛出的错误,是因为它没有通过异步模式捕获错误。从这
里就可以看出期约真正的异步特性:它们是同步对象(在同步执行模式中使用),但也是异步执行模式
的媒介。
在前面的例子中,拒绝期约的错误并没有抛到执行同步代码的线程里,而是通过浏览器异步消息队
列来处理的。因此,try/catch 块并不能捕获该错误。代码一旦开始以异步模式执行,则唯一与之交互
的方式就是使用异步结构——更具体地说,就是期约的方法。

3. Promise实例方法

3.1 实现Thenable接口

  1. class MyThenable {
  2. then() {}
  3. }

3.2 Promise.prototype.then()

  1. function onResolved(id) {
  2. setTimeout(console.log, 0, id, 'resolved');
  3. }
  4. function onRejected(id) {
  5. setTimeout(console.log, 0, id, 'rejected');
  6. }
  7. let p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000));
  8. let p2 = new Promise((resolve, reject) => setTimeout(reject, 3000));
  9. p1.then(() => onResolved('p1'),
  10. () => onRejected('p1'));
  11. p2.then(() => onResolved('p2'),
  12. () => onRejected('p2'));
  13. // (after 3s)
  14. // p1 resolved
  15. // p2 rejected
  16. // PromiseThenExample01.js

因为期约只能转换为最终状态一次,所以这两个操作一定是互斥的。
如前所述,两个处理程序参数都是可选的。而且,传给 then()的任何非函数类型的参数都会被静
默忽略。如果想只提供 onRejected 参数,那就要在 onResolved 参数的位置上传入 undefined。这
样有助于避免在内存中创建多余的对象,对期待函数参数的类型系统也是一个交代。

  1. function onResolved(id) {
  2. setTimeout(console.log, 0, id, 'resolved');
  3. }
  4. function onRejected(id) {
  5. setTimeout(console.log, 0, id, 'rejected');
  6. }
  7. let p1 = new Promise((resolve, reject) => setTimeout(resolve, 3000));
  8. let p2 = new Promise((resolve, reject) => setTimeout(reject, 3000));
  9. // Non-function handlers are silently ignored, not recommended
  10. p1.then('gobbeltygook');
  11. // Canonical form of explicit onResolved handler skipping
  12. p2.then(null, () => onRejected('p2'));
  13. // p2 rejected (after 3s)
  14. // PromiseThenExample02.js
  1. let p1 = new Promise(() => {});
  2. let p2 = p1.then();
  3. setTimeout(console.log, 0, p1); // Promise <pending>
  4. setTimeout(console.log, 0, p2); // Promise <pending>
  5. setTimeout(console.log, 0, p1 === p2); // false

这个新期约实例基于 onResovled 处理程序的返回值构建。换句话说,该处理程序的返回值会通过
Promise.resolve()包装来生成新期约。如果没有提供这个处理程序,则 Promise.resolve()就会
包装上一个期约解决之后的值。如果没有显式的返回语句,则 Promise.resolve()会包装默认的返回
值 undefined。

  1. let p1 = Promise.resolve('foo');
  2. // Calling then() with no handler function acts as a passthrough
  3. let p2 = p1.then();
  4. setTimeout(console.log, 0, p2); // Promise <resolved>: foo
  5. // These are equivalent
  6. let p3 = p1.then(() => undefined);
  7. let p4 = p1.then(() => {});
  8. let p5 = p1.then(() => Promise.resolve());
  9. setTimeout(console.log, 0, p3); // Promise <resolved>: undefined
  10. setTimeout(console.log, 0, p4); // Promise <resolved>: undefined
  11. setTimeout(console.log, 0, p5); // Promise <resolved>: undefined
  12. // PromiseThenExample03.js
  1. ...
  2. // These are equivalent:
  3. let p6 = p1.then(() => 'bar');
  4. let p7 = p1.then(() => Promise.resolve('bar'));
  5. setTimeout(console.log, 0, p6); // Promise <resolved>: bar
  6. setTimeout(console.log, 0, p7); // Promise <resolved>: bar
  7. // Promise.resolve() preserves the returned promise
  8. let p8 = p1.then(() => new Promise(() => {}));
  9. let p9 = p1.then(() => Promise.reject());
  10. // Uncaught (in promise): undefined
  11. setTimeout(console.log, 0, p8); // Promise <pending>
  12. setTimeout(console.log, 0, p9); // Promise <rejected>: undefined
  13. // PromiseThenExample03.js
  1. ...
  2. let p10 = p1.then(() => { throw 'baz'; });
  3. // Uncaught (in promise) baz
  4. setTimeout(console.log, 0, p10); // Promise <rejected> baz
  5. PromiseThenExample03.js
  1. ...
  2. let p11 = p1.then(() => Error('qux'));
  3. setTimeout(console.log, 0, p11); // Promise <resolved>: Error: qux
  4. PromiseThenExample03.js

3.3 Promise.prototype.catch()

3.4 Promise.prototype.finally()

3.5 非重入期约方法

3.6 邻近处理程序的执行顺序

3.7 传递解决值和拒绝理由

3.8 拒绝期约与拒绝错误处理

4. Promise连锁与Promise合成

5. Promise扩展