从零开始,手写完整的Promise原理

实现Promise

Posted by ZhuLang on February 19, 2025

Promises/A+规范

1. 异步回调

1.1 回调地狱

在执行多个操作时,回调函数的嵌套可能会导致代码难以理解,这种现象通常被称为回调地狱

1.2 并行结果

当多个异步操作之间没有顺序要求时,可以并行执行以节省时间。然而,当必须等到所有异步操作完成后才能执行后续任务时,通常难以实现并行执行。

2. Promise

Promise 的本意是承诺,在程序中表示一段时间后将会给出结果。什么时候需要‘等待一段时间’?通常是在处理异步操作时,比如网络请求或读取本地文件

3. Promise 的三种状态

  • Pending 实例创建时的初始状态
  • Fulfilled 成功状态
  • Rejected 失败状态

Promise 对象有三种状态:Pending(初始状态),Fulfilled(成功状态),和 Rejected(失败状态)。当状态改变时,then 方法可以指定相应的回调函数,resolve 用于执行第一个函数(onFulfilled),而 reject 则执行第二个函数(onRejected)。

4. 构建一个 Promise

1
2
3
4
5
6
7
8
9
10
11
12
let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (Math.random() > 0.5) {
      resolve('成功');
    } else {
      reject('失败');
    }
  }, 1000);
});
promise.then((data) => {
  console.log(data);
});
  • 构造一个 Promise 实例时,需要传入一个包含两个函数参数的回调函数
  • 传入的回调函数需要有两个参数,通常被命名为 resolvereject,它们分别表示操作成功和失败后的处理逻辑。resolve 使 Promise 状态变为成功,同时传递成功后的结果;reject 则使 Promise 状态变为失败,并传递错误信息。

5. Promise 的链式调用

  • 每次调用 then 都会返回一个新的 Promise 实例,这使得链式调用成为可能。链式调用中,前一个 then 返回的值会作为参数传递给下一个 then 方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
readFile('1.txt')
  .then(function (data) {
    console.log(data);
    return data;
  })
  .then(function (data) {
    console.log(data);
    return readFile(data);
  })
  .then(function (data) {
    console.log(data);
  })
  .catch(function (err) {
    console.log(err);
  });

6. Promise API

Promise.all

  • Promise.all 方法接收一个包含多个 Promise 实例的数组,并返回一个新的 Promise 实例。返回的 Promise 状态取决于数组中所有 Promise 实例的状态。当所有实例都处于 resolve 状态时,返回的 Promise 也会变为 resolve 状态;如果任意一个实例处于 reject 状态,返回的 Promise 也会变为 reject 状态
1
2
3
4
5
Promise.all([p1, p2]).then(function (result) {
  console.log(result); // [ '2.txt', '2' ]
});

// 不管两个promise谁先完成,Promise.all方法会按照数组里面的顺序将结果返回

Promise.race

  • Promise.race 方法接收一个包含多个 Promise 实例的数组,并返回一个新的 Promise 实例。返回的 Promise 状态取决于第一个改变状态的实例。当任意一个实例变为 resolve 时,返回的 Promise 也会变为 resolve 状态;如果任意一个实例变为 reject,返回的 Promise 也会变为 reject 状态
1
2
3
Promise.race([p1, p2]).then(function (result) {
  console.log(result); // [ '2.txt', '2' ]
});

Promise.resolve

  • Promise.resolve 方法返回一个 resolve 状态的 Promise 实例。根据传入参数的不同,该方法可能会直接返回传入的值,或者是一个已经 resolve 的 Promise 实例

Promise.reject

  • Promise.reject 方法返回一个 reject 状态的 Promise 实例。通常会将抛出的错误信息作为参数传递给这个实例。

7. Promise/A+完整实现

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
// TypeScript实现
export class PfPromise {
  private status: 'pending' | 'resolved' | 'rejected' = 'pending';
  private value: any;
  private onResolvedCallbacks: Array<(value: any) => void> = [];
  private onRejectedCallbacks: Array<(reason: any) => void> = [];

  constructor(
    executor: (
      resolve: (value: any) => void,
      reject: (reason: any) => void
    ) => void
  ) {
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }

  private resolve(value: any) {
    if (value instanceof PfPromise) {
      return value.then(this.resolve.bind(this), this.reject.bind(this));
    }
    setTimeout(() => {
      if (this.status === 'pending') {
        this.status = 'resolved';
        this.value = value;
        this.onResolvedCallbacks.forEach((callback) => callback(value));
      }
    });
  }

  private reject(reason: any) {
    setTimeout(() => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.value = reason;
        this.onRejectedCallbacks.forEach((callback) => callback(reason));
      }
    });
  }

  then(onFulfilled?: (value: any) => any, onRejected?: (reason: any) => any) {
    return new PfPromise((resolve, reject) => {
      const fulfilledCallback = () => {
        try {
          const result = onFulfilled ? onFulfilled(this.value) : this.value;
          resolvePromise(result, resolve, reject);
        } catch (error) {
          reject(error);
        }
      };

      const rejectedCallback = () => {
        try {
          const result = onRejected ? onRejected(this.value) : this.value;
          resolvePromise(result, resolve, reject);
        } catch (error) {
          reject(error);
        }
      };

      if (this.status === 'resolved') {
        setTimeout(fulfilledCallback);
      } else if (this.status === 'rejected') {
        setTimeout(rejectedCallback);
      } else {
        this.onResolvedCallbacks.push(fulfilledCallback);
        this.onRejectedCallbacks.push(rejectedCallback);
      }
    });
  }

  catch(onRejected: (reason: any) => any) {
    return this.then(undefined, onRejected);
  }

  static all(promises: PfPromise[]) {
    return new PfPromise((resolve, reject) => {
      const results: any[] = [];
      let completed = 0;

      promises.forEach((promise, index) => {
        promise
          .then((data) => {
            results[index] = data;
            completed++;
            if (completed === promises.length) {
              resolve(results);
            }
          })
          .catch(reject);
      });
    });
  }

  static race(promises: PfPromise[]) {
    return new PfPromise((resolve, reject) => {
      promises.forEach((promise) => {
        promise.then(resolve).catch(reject);
      });
    });
  }

  static resolve(value: any) {
    return new PfPromise((resolve) => resolve(value));
  }

  static reject(reason: any) {
    return new PfPromise((_, reject) => reject(reason));
  }
}

function resolvePromise(
  x: any,
  resolve: (value: any) => void,
  reject: (reason: any) => void,
  visited = new Set<any>()
) {
  if (visited.has(x)) {
    return reject(new TypeError('循环引用'));
  }

  if (x instanceof PfPromise) {
    visited.add(x);
    x.then(
      (value) => resolvePromise(value, resolve, reject, visited),
      (reason) => reject(reason)
    );
  } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let then;
    try {
      then = x.then;
    } catch (error) {
      return reject(error);
    }

    if (typeof then === 'function') {
      let called = false;
      try {
        visited.add(x);
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(y, resolve, reject, visited);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (error) {
        if (!called) {
          reject(error);
        }
      }
    } else {
      resolve(x);
    }
  } else {
    resolve(x);
  }
}

源码