# JavaScript中的Promise详解

# 1. 手写Promise实现

# 1.1 Promise基本声明

Promise是一个类,它的构造函数接受一个函数作为参数,该函数的两个参数也都是函数

class MyPromise {
  // 构造器
  constructor(executor) {
    const resolve = () => {};
    const reject = () => {};
    // 立即执行
    executor(resolve, reject);
  }
}
1
2
3
4
5
6
7
8
9

# 1.2 Promise的三种状态

Promise存在三个状态(state):

  1. pending(待定):Promise初始状态,在resolve或reject前处于此状态
  2. fulfilled(已成功):Promise被resolve后处于此状态,状态不能再改变,且必须拥有一个不可变的值(value)
  3. rejected(已失败):Promise被reject后处于此状态,状态也不能再改变,且必须拥有一个不可变的拒绝原因(reason)
class MyPromise {
  constructor(executor) {
    // 初始化state为等待态
    this.state = 'pending';
    // 成功的值
    this.value = undefined;
    // 失败的原因
    this.reason = undefined;
    const resolve = (value) => {
      // state改变,resolve调用就会失败
      if (this.state === 'pending') {
        // resolve调用后,state转化为成功态
        this.state = 'fulfilled';
        // 储存成功的值
        this.value = value;
      }
    };
    const reject = (reason) => {
      // state改变,reject调用就会失败
      if (this.state === 'pending') {
        // reject调用后,state转化为失败态
        this.state = 'rejected';
        // 储存失败的原因
        this.reason = reason;
      }
    };
    // 如果executor执行报错,直接执行reject
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
}
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

# 1.3 then方法

Promise有一个叫做then的方法,then方法是定义在原型对象Promise.prototype上的方法,它的作用是为Promise实例添加状态改变时的回调函数。

then方法接受两个参数:

  • onFulfilled:当Promise对象状态变为fulfilled时调用
  • onRejected:当Promise对象状态变为rejected时调用
class MyPromise {
  constructor(executor) {
    // ...
  }
  // then 方法 有两个参数onFulfilled onRejected
  then(onFulfilled, onRejected) {
    // 状态为fulfilled,执行onFulfilled,传入成功的值
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    }
    // 状态为rejected,执行onRejected,传入失败的原因
    if (this.state === 'rejected') {
      onRejected(this.reason);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const test = new MyPromise((resolve, reject) => {
  resolve('成功');
}).then((res) => console.log(res), (err) => console.log(err));
// 成功
1
2
3
4

# 1.4 静态方法

是当resolve在setTimeout内执行,then时state还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 成功存放的数组
    this.onResolvedCallbacks = [];
    // 失败存放法数组
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        // 一旦resolve执行,调用成功数组的函数
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        // 一旦reject执行,调用失败数组的函数
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    }
    if (this.state === 'rejected') {
      onRejected(this.reason);
    }
    // 当状态state为pending时
    if (this.state === 'pending') {
      // onFulfilled传入到成功数组
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value);
      });
      // onRejected传入到失败数组
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}
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
const test = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功');
  });
}).then((res) => console.log(res), (err) => console.log(err));
console.log('失败');
// 如果是1.4 由于立即执行函数,没有办法解决异步执行resolve代码,返回 失败
// 失败 -> 成功
1
2
3
4
5
6
7
8

# 1.5 链式调用实现

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

  • 当Promise成功执行时,所有onFulfilled回调需按照其注册顺序依次执行
  • 当Promise被拒绝执行时,所有onRejected回调需按照其注册顺序依次执行

then方法必须返回一个新的Promise对象,以实现链式调用:

  1. 如果onFulfilled或onRejected返回一个值x,则运行Promise解决过程:[Resolve](promise2, x) (opens new window) resolvePromise
  2. 如果onFulfilled或onRejected抛出一个异常e,则promise2必须拒绝执行,并返回拒因e
  3. 如果onFulfilled不是函数且promise1成功执行,promise2必须成功执行并返回相同的值
  4. 如果onRejected不是函数且promise1拒绝执行,promise2必须拒绝执行并返回相同的据因
class MyPromise {
  constructor(executor) {
    // ...
  }
  then(onFulfilled, onRejected) {
    // 声明返回的promise2
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        const x = onFulfilled(this.value);
        // resolvePromise函数,处理自己return的promise和默认的promise2的关系
        resolvePromise(promise2, x, resolve, reject);
      }
      if (this.state === 'rejected') {
        const x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      }
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        });
        this.onRejectedCallbacks.push(() => {
          const x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        });
      }
    });
    // 返回promise,完成链式
    return promise2;
  }
}
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

# 1.6 resolvePromise函数详解

resolvePromise函数用于处理then方法返回值x的不同情况:

  1. 基础类型值处理

    • 如果x是普通值(非对象或函数),直接调用resolve(x)
    • x不能是null
  2. 对象或函数处理(包括Promise):

    • 如果x是对象或函数,声明let then = x.then
    • 如果取then属性时报错,则调用reject()
    • 如果then是函数,则使用call执行then,第一个参数是this,后面是成功回调和失败回调
    • 如果成功回调返回的还是Promise,就递归继续解析
  3. 防止多次调用

    • 成功和失败回调只能调用一个,所以设定一个called变量来防止多次调用
/**
 * Promise解决过程 [[Resolve]](promise, x)
 * @param {Promise} promise2 - then方法返回的新Promise对象
 * @param {*} x - then方法回调函数的返回值
 * @param {Function} resolve - promise2的resolve函数
 * @param {Function} reject - promise2的reject函数
 */
function resolvePromise(promise2, x, resolve, reject) {
  // 规范 2.3.1,x 不能和 promise2 相同,避免循环引用
  if (x === promise2) {
    // reject报错
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  // 规范 2.3.2
  // 如果 x 为 Promise,状态为 pending 需要继续等待否则执行
  if (x instanceof MyPromise) {
    // 2.3.2.1 如果x为pending状态,promise必须保持pending状态,直到x为fulfilled/rejected
    if (x.currentState === 'pending') {
      x.then((value) => {
        // 再次调用该函数是为了确认 x resolve 的
        // 参数是什么类型,如果是基本类型就再次 resolve
        // 把值传给下个 then
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else { // 但如果这个promise的状态已经确定了,那么它肯定有一个正常的值,而不是一个thenable,所以这里可以取它的状态
      x.then(resolve, reject);
    }
    return;
  }

  // 防止多次调用
  let called;

  // 规范 2.3.3,判断 x 是否为对象或函数
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 规范 2.3.3.2,如果不能取出 then,就 reject
    try {
      // 规范2.3.3.1 因为x.then可能是一个getter,这种情况下多次读取就有可能产生副作用
      // 既要判断它的类型,又要调用它,这就是两次读取
      const { then } = x;

      // 规范2.3.3.3,如果 then 是函数,调用 x.then
      if (typeof then === 'function') {
        // 规范 2.3.3.3
        // reject 或 reject 其中一个执行过的话,忽略其他的
        then.call(x, (y) => {
          // 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准
          if (called) return;
          called = true;

          // resolve的结果依旧是promise 那就继续解析
          resolvePromise(promise2, y, resolve, reject);
        }, (err) => {
          if (called) return;
          called = true;
          reject(err); // 失败了就失败了
        });
      } else {
        resolve(x); // 直接成功即可
      }
    } catch (e) {
      // 也属于失败
      if (called) return;
      called = true;

      // 取then出错了那就不要在继续执行了
      reject(e);
    }
  } else {
    resolve(x);
  }
}
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

# 1.7 其他问题

# 2.1 onFulfilled和onRejected参数可选性

  • onFulfilled和onRejected都是可选参数
  • 如果类型不是函数需要忽略,后续如果还有then的回调会继续执行
  • 都有默认值,具体如下:
    • onFulfilled的默认值是value => value
    • onRejected的默认值是err => { throw err; }
  • 异步调用要求
    • 规定onFulfilled或onRejected不能同步被调用,必须异步调用
    • 我们使用setTimeout解决异步问题,确保回调在下一个事件循环中执行
    • 如果onFulfilled或onRejected报错,则直接返回reject()
class MyPromise {
  constructor(executor) {
    // ...
  }
  then(onFulfilled, onRejected) {
    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误

    onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
      throw err;
    };
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        // 异步
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.state === 'rejected') {
        // 异步
        setTimeout(() => {
          // 如果报错
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          // 至于为什么用 setTimeout?因为我们模拟不了微任务,那就用宏任务去解决吧
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          // 异步
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    // 返回promise,完成链式
    return promise2;
  }
}
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

# 1.8 测试Promise

为了确保我们实现的MyPromise符合Promise/A+规范,我们需要使用官方测试套件进行验证。

# 1.8.1 实现deferred方法

测试套件要求实现一个静态方法deferred,用于创建一个包含resolve、reject方法和promise对象的结构。该方法的实现如下:

/**
 * 创建一个deferred对象,包含promise、resolve和reject属性
 * 用于Promise/A+测试套件
 * @returns {Object} deferred对象
 * @property {MyPromise} promise - Promise实例
 * @property {Function} resolve - resolve函数
 * @property {Function} reject - reject函数
 */
// eslint-disable-next-line no-multi-assign
MyPromise.defer = MyPromise.deferred = function () {
  const dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

module.exports = MyPromise;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 1.8.2 运行Promises/A+测试套件

首先全局安装Promise/A+测试套件:

npm install promises-aplus-tests -g
1

然后在package.json中添加测试脚本:

{
  "devDependencies": {
    "promises-aplus-tests": "^2.1.2"
  },
  "scripts": {
    "test": "promises-aplus-tests MyPromise"
  }
}
1
2
3
4
5
6
7
8

执行测试命令:

npm test
1

通过测试后,可以确保我们的MyPromise实现完全符合Promise/A+规范。

# 1.9 Promise.all

MyPromise.all = function(promiseList) {
  const resPromise = new MyPromise((resolve, reject) => {
    let count = 0;
    const result = [];
    const { length } = promiseList;

    if (length === 0) {
      return resolve(result);
    }

    promiseList.forEach((promise, index) => {
      MyPromise.resolve(promise).then((value) => {
        count += 1;
        result[index] = value;
        if (count === length) {
          resolve(result);
        }
      }, (reason) => {
        reject(reason);
      });
    });
  });

  return resPromise;
};
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

# 1.10 Promise.race

MyPromise.race = function(promiseList) {
  const resPromise = new MyPromise((resolve, reject) => {
    const { length } = promiseList;

    if (length === 0) {
      return resolve();
    }
    for (let i = 0; i < length; i++) {
      MyPromise.resolve(promiseList[i]).then((value) => {
        return resolve(value);
      }, (reason) => {
        return reject(reason);
      });
    }
  });
  return resPromise;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 1.11 完整代码

function resolvePromise(promise2, x, resolve, reject) {
  // 规范 2.3.1,x 不能和 promise2 相同,避免循环引用
  if (x === promise2) {
    // reject报错
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // 规范 2.3.2
  // 如果 x 为 Promise,状态为 pending 需要继续等待否则执行
  if (x instanceof MyPromise) {
    // 2.3.2.1 如果x为pending状态,promise必须保持pending状态,直到x为fulfilled/rejected
    if (x.currentState === 'pending') {
      x.then((value) => {
        // 再次调用该函数是为了确认 x resolve 的
        // 参数是什么类型,如果是基本类型就再次 resolve
        // 把值传给下个 then
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else { // 但如果这个promise的状态已经确定了,那么它肯定有一个正常的值,而不是一个thenable,所以这里可以取它的状态
      x.then(resolve, reject);
    }
    return;
  }
  // 防止多次调用
  let called;
  // 规范 2.3.3,判断 x 是否为对象或函数
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 规范 2.3.3.2,如果不能取出 then,就 reject
    try {
      // 规范2.3.3.1 因为x.then可能是一个getter,这种情况下多次读取就有可能产生副作用
      // 既要判断它的类型,又要调用它,这就是两次读取
      const { then } = x;
      // 规范2.3.3.3,如果 then 是函数,调用 x.then
      if (typeof then === 'function') {
        // 规范 2.3.3.3
        // reject 或 reject 其中一个执行过的话,忽略其他的
        then.call(x, (y) => {
          // 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准
          if (called) return;
          called = true;
          // resolve的结果依旧是promise 那就继续解析
          resolvePromise(promise2, y, resolve, reject);
        }, (err) => {
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        });
      } else {
        resolve(x); // 直接成功即可
      }
    } catch (e) {
      // 也属于失败
      if (called) return;
      called = true;
      // 取then出错了那就不要在继续执行了
      reject(e);
    }
  } else {
    resolve(x);
  }
}
class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 成功存放的数组
    this.onResolvedCallbacks = [];
    // 失败存放法数组
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        // 一旦resolve执行,调用成功数组的函数
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        // 一旦reject执行,调用失败数组的函数
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误

    onRejected = typeof onRejected === 'function' ? onRejected : (err) => {
      throw err;
    };
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        // 异步
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.state === 'rejected') {
        // 异步
        setTimeout(() => {
          // 如果报错
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          // 至于为什么用 setTimeout?因为我们模拟不了微任务,那就用宏任务去解决吧
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          // 异步
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    // 返回promise,完成链式
    return promise2;
  }
}
MyPromise.resolve = function(val) {
  return new MyPromise((resolve, reject) => {
    resolve(val);
  });
};
MyPromise.reject = function(val) {
  return new MyPromise((resolve, reject) => {
    reject(val);
  });
};
MyPromise.race = function(promiseList) {
  const resPromise = new MyPromise((resolve, reject) => {
    const { length } = promiseList;

    if (length === 0) {
      return resolve();
    }
    for (let i = 0; i < length; i++) {
      MyPromise.resolve(promiseList[i]).then((value) => {
        return resolve(value);
      }, (reason) => {
        return reject(reason);
      });
    }
  });
  return resPromise;
};
MyPromise.all = function(promiseList) {
  const resPromise = new MyPromise((resolve, reject) => {
    let count = 0;
    const result = [];
    const { length } = promiseList;

    if (length === 0) {
      return resolve(result);
    }

    promiseList.forEach((promise, index) => {
      MyPromise.resolve(promise).then((value) => {
        count += 1;
        result[index] = value;
        if (count === length) {
          resolve(result);
        }
      }, (reason) => {
        reject(reason);
      });
    });
  });

  return resPromise;
};
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

# 2. Promise面试题

# 2.1 promise基础

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    reject('fail');
  }, 0);
  resolve();
  console.log(2);
});
promise.then(() => {
  console.log(3);
}).catch(() => {
  console.log(4);
});
console.log(5);
// 输出结果 1 2 5 3
// Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的
// 构造函数中的 resolve 或 reject 只有第一次执行有效,状态一旦改变则不能再变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2.2 promise 链式调用

Promise.resolve(1)
  .then((res) => {
    console.log(res);

    throw Error('Oh no!');
    // eslint-disable-next-line no-unreachable
    return 2;
  })
  .then((res) => {
    // 由于报错跳过
    console.log(res);
    return 3;
  })
  .catch((err) => {
    console.log('fail', err);
    return 4;
  })
  .catch((err) => {
    // 由于返回 resolve 跳过
    console.log(err);
    return 5;
  })
  .then((res) => {
    console.log(res);
  })
  .then((res) => {
    // 上一个then返回的值为undefined
    console.log(res);
  });
// 输出结果 1 fail Error: Oh no! 4 undefined
// promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用
// 如果当前步骤遇到错误,则任何后续的 .then  都将被跳过,直到遇到 .catch
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
Promise.resolve()
  .then(() => {
    return new Error('Oh no!');
  })
  .then((res) => {
    console.log('then: ', res);
  })
  .catch((err) => {
    console.log('catch: ', err);
  });
// 输出结果 then: Oh no!
// .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获,需要改成其中一种
// return Promise.reject(new Error('Oh no!'))
// throw Error('Oh no!')
// 因为返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('Oh no!') 等价于 return Promise.resolve(new Error('Oh no!'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const promise = Promise.resolve()
  .then(() => {
    return promise;
  });
promise.catch(console.error);
// 输出结果 TypeError: Chaining cycle detected for promise
// .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
1
2
3
4
5
6
7
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log);
// 1
// .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透
1
2
3
4
5
6
  • Promise的状态一经改变就不能再改变
  • .then和.catch都会返回一个新的Promise
  • catch不管被连接到哪里,都能捕获上层未捕捉过的错误
  • 在Promise中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如return 2会被包装为return Promise.resolve(2)。
  • Promise 的 .then 或者 .catch 可以被调用多次, 但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值。
  • .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获
  • .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
  • .then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传
  • .then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法
  • .finally方法也是返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数
Promise.resolve('1')
  .then((res) => {
    console.log(res);
    return 2;
  })
  .then((res) => {
    console.log(res);
    return 3;
  })
  .finally(() => {
    console.log('finally');
  });
Promise.resolve('4')
  .then((res) => {
    console.log('finally2', res);
    return 5;
  })
  .finally(() => {
    console.log('finally2');
    return 6;
  })
  .then((res) => {
    console.log('finally2', res);
  });
// 1 finally2 4  2  finally2  finally finally2 5
// finally与then和catch相同,都是返回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

# 2.3 setTimeout相关

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once');
    resolve('success');
  }, 1000);
});
const start = Date.now();
promise.then((res) => {
  console.log(res, Date.now() - start);
});
promise.then((res) => {
  console.log(res, Date.now() - start);
});
// once success 1005 success 1007
// promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,
// 那么后续每次调用 .then 或者 .catch 都会直接拿到该值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2.4 race all等

function runAsync (x) {
  const p = new Promise((r) => setTimeout(() => r(x, console.log(x)), 1000));
  return p;
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then((res) => console.log('result: ', res))
  .catch((err) => console.log(err));
// 1 'result: ' 1 2 3
// 使用.race()方法,它只会获取最先执行完成的那个结果,其它的异步任务虽然也会继续进行下去,不过race已经不管那些任务的结果了
1
2
3
4
5
6
7
8
9

# 2.5 async/await

async function async1 () {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
  setTimeout(() => {
    console.log('timer1');
  }, 0);
}
async function async2 () {
  setTimeout(() => {
    console.log('timer2');
  }, 0);
  console.log('async2');
}
async1();
setTimeout(() => {
  console.log('timer3');
}, 0);
console.log('start');
// async1 start => async2 => start => async1 end => timer2 => timer3 => timer1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success';
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2');
    reject('error');
  });
}
async1().then((res) => console.log(res));
// 'async2'  => Uncaught (in promise) error
// 如果在async函数中抛出了错误,则终止错误结果,不会继续向下执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.6 综合

const async1 = async () => {
  console.log('async1');
  setTimeout(() => {
    console.log('timer1');
  }, 2000);
  await new Promise((resolve) => {
    console.log('promise1');
  });
  console.log('async1 end');
  return 'async1 success';
};
console.log('script start');
async1().then((res) => console.log(res));
console.log('script end');
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .catch(4)
  .then((res) => console.log(res));
setTimeout(() => {
  console.log('timer2');
}, 1000);
// script start => async1 => promise1 => script end => 1 => timer2 => timer1
// async函数中await的new 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

# 3. 参考文章

BAT前端经典面试问题:史上最最最详细的手写Promise教程 (opens new window)

手写PromiseA+ (opens new window)

Promise 必知必会(十道题) (opens new window)

【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理 (opens new window)

Last Updated: 9/2/2025, 3:07:38 PM