本文主要讲解 Promise 的常用方法以及使用技巧,并实现如何手写一个 Promise。

Promise 的出现

回调地狱问题

在传统的 JS 编程中始终存在一个问题,即当当前回调函数的执行依赖于上一个回调函数的执行结果的时候,会形成回调函数层层嵌套的问题,严重影响代码的可读性与可维护性,这种现象一般称之为回调地狱

下面为示例代码,回调地狱的一个比较常见的情景为ajax请求,即下一个请求的是否发起依赖于上一个请求的结果。

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 xhr = new XMLHttpRequest();
xhr.open(
  "get",
  "https://v0.yiketianqi.com/api?unescape=1&version=v61&appid=82294778&appsecret=4PKVFula&city=%E5%8C%97%E4%BA%AC"
);
xhr.send();
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log(xhr.responseText);

      //伪代码....
      let xhr = new XMLHttpRequest();
      xhr.open("get", "http://www.xx.com?a" + xhr.responseText);
      xhr.send();
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            console.log(xhr.responseText);
          }
        }
      };
    }
  }
};

Promise 的出现

  1. Promise是什么?其解决了什么问题?
    Promise 是异步编程的一种解决方案,比传统的解决方案回调函数更合理、更强大。ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。指定回调函数的方式也变得更加灵活易懂,也解决了异步回调地狱的问题旧方案是单纯使用回调函数,常见的异步操作有:定时器、fs 模块、ajax、数据库操作。从语法上说,Promise 是一个构造函数;从功能上说,Promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值。
  2. Promise设计的核心理念是什么?
    • Promise的角度来说,是将状态改变与回调函数彻底区分开。
    • 从应用Promise的角度来说,以ajax请求数据为例,则是将数据请求与数据处理区分开。

Promise 的实际应用

案例 1:利用 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
//1.普通文件读取方式
const fs = require("fs");

//2.直接利用readfile来进行读取
/* fs.readFile(__dirname + '/data.txt',(err,data)=>{
    if(err) throw err;
    console.log(data.toString());
}) */

//3.利用promise来实现文件的读取
const p = new Promise((resolve, reject) => {
  fs.readFile(__dirname + "/data.txt", (err, data) => {
    if (err) {
      reject(err);
    } else {
      resolve(data);
    }
  });
});

p.then(
  (value) => {
    console.log(value.toString());
  },
  (reason) => {
    console.log(reason);
  }
);

案例 2:利用 promise 进行 ajax 请求

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
<body>
  <button>发送ajax请求</button>
  <script>
    //1.获取DOM元素对象
    let btn = document.querySelector("button");
    //2.绑定事件
    btn.onclick = function () {
      //3.创建promise实例对象
      const p = new Promise((resolve, reject) => {
        //4.创建ajax实例对象
        const xhr = new XMLHttpRequest();
        //5.打开请求
        xhr.open(
          "get",
          "https://www.yiketianqi.com/free/day?appid=82294778&appsecret=4PKVFula&unescape=1"
        );
        //6.发送请求
        xhr.send();
        //7.利用onreadystatechange事件
        xhr.onreadystatechange = function () {
          //8.判断
          if (xhr.readyState == 4) {
            if (xhr.status == 200) {
              resolve(xhr.responseText);
            } else {
              reject(xhr.response);
            }
          }
        };
      });
      p.then(
        (value) => {
          console.log(JSON.parse(value));
        },
        (reason) => {
          console.log("获取信息失败");
        }
      );
    };
  </script>
</body>

案例 3:利用 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
const mongoose = require("mongoose");

new Promise((resolve, reject) => {
  mongoose.connect("mongodb://127.0.0.1/project");
  mongoose.connection.on("open", () => {
    //连接成功的情况
    resolve();
  });

  mongoose.connection.on("error", () => {
    //连接失败的情况
    reject();
  });
}).then(
  (value) => {
    //创建结构
    const NoteSchema = new mongoose.Schema({
      title: String,
      content: String,
    });
    //创建模型
    const NoteModel = mongoose.model("notes", NoteSchema);

    //读取操作
    NoteModel.find().then(
      (value) => {
        console.log(value);
      },
      (reason) => {
        console.log(reason);
      }
    );
  },
  (reason) => {
    console.log("连接失败");
  }
);

案例 4:封装一个函数,作用是读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const fs = require("fs");

function ReadFileFun(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, (err, data) => {
      //判断
      if (err) {
        reject(err);
      } else {
        resolve(data);
      }
    });
  });
}

ReadFileFun("./data.txt").then(
  (value) => {
    console.log(value.toString());
  },
  (reason) => {
    console.log(reason);
  }
);

node 中的 promisify

  • promisify (只能在 NodeJS 环境中使用)
  • promisifyutil 模块中的一个方法 utilnodeJS 的内置模块
  • 作用: 返回一个新的函数, 函数是 promise 风格的.
1
2
3
4
5
6
7
8
9
10
11
12
13
const util = require("util");
const fs = require("fs");
//通过 fs.readFile 创建一个新的函数
const mineReadFile = util.promisify(fs.readFile);

mineReadFile("./resource/2.html").then(
  (value) => {
    console.log(value.toString());
  },
  (reason) => {
    console.log(reason);
  }
);
查看 promisify 的手写实现

看到 promisify 这个函数对其内部实现机制比较感兴趣,那我们就来手写一下。

  1. 首先promisify函数得返回一个函数,同时返回的这个函数应该返回一个Promise对象,根据这两点我们把函数的基本结构搭建起来。

    1
    2
    3
    4
    5
    const promisify = () => {
      return function () {
        return new Promise((resolve, reject) => {});
      };
    };
  2. 利用返回的这个Promise对象,可以使用then方法进行回调的处理,即原来函数的回调决定这Promise状态的改变以及执行,所以需要将改变Promise对象的状态的回调函数传入参数中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
     * @param {(...args) => void} func
     * @returns {(...args) => Promise<any}
     */
    const promisify = (func) => {
      return function (...params) {
        return new Promise((resolve, reject) => {
          func.call(this, ...params, (err, data) => {
            if (err) reject(err);
            else resolve(data);
          });
        });
      };
    };

Promise 的实例方法

then

注意点:通过then返回的promise的对象的状态由谁决定。

  • 若返回非promise对象或者什么都不返回则,状态为fullfilled
  • 若函数执行过程中抛出错误,则状态为rejected
  • 若返回一个promise对象,则状态与返回的promise对象保持一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const p = new Promise((resolve, reject) => {
  resolve("ok");
});

let result = p.then(
  (value) => {
    throw "错误";
  },
  (reason) => {
    console.log(reason);
  }
);

console.log(result);

catch

能够穿透捕获Promise中的错误,其实相当于Promise.prototype.then(undefined, onRejected)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let p = new Promise((resolve, reject) => {
  //resolve('success');
  reject("error");
});

p.catch((reason) => {
  console.log(reason);
});

//then方法中不是必须传入两个参数,可以只传递成功时的回调函数
//也可以单独使用catch来指定失败的回调函数

//异常(错误)穿透
//当如果有多个需要执行的成功时的回调函数,可以不需要每一次都写失败回调,可以统一最后利用catch
//当如果promise对象的状态为reject的话,会一直向下穿透直到catch方法
p.then((value) => {
  console.log(value);
})
  .then((value) => {
    console.log(value);
  })
  .catch((reason) => {
    console.log(reason);
  });

finally

finally 是在 ES9(ES2018)中新增的一个特性:表示无论 Promise 对象变成 fufilled 还是 rejected 状态,最终都会被执行。
finally 方法中的回调函数是不接受参数的,因为无论前面是 fulfilled 状态还是 rejected 状态, 它都是执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
const p = new Promise((resolve, reject) => {
  // resolve('ok');
  reject("error");
});
p.then((res) => {
  console.log(res);
})
  .catch((err) => {
    console.log(err);
  })
  .finally(() => {
    console.log("finally");
  });

Promise 类静态方法

resolve

  1. 当参数不是Promise对象的时候,返回promise状态为fullfilled
  2. 当参数是Promise对象的时候,返回的Promise实例的属性由该Promise实例决定。
1
2
3
4
5
6
7
8
9
let p3 = Promise.resolve(
  new Promise((resolve, reject) => {
    resolve("success");
  })
);
console.log(p3);

let p4 = Promise.resolve(Promise.resolve(Promise.resolve("OK")));
console.log(p4);

reject

始终返回状态为rejectedPromise对象

1
2
console.log(Promise.reject(123));
console.log(Promise.reject(Promise.resolve("ok")));

all

接受一个Promise对象的数组,如果数组中的Promise对象的状态都是fullfilled,则返回的也是一个状态为fullfilled对象,PromiseResult的值为每个成功值的数组。若存在状态为rejected的对象则返回一个状态为rejectedPromise对象,PromiseResult为第一个状态为rejectedPromise对象。

1
2
3
4
5
6
7
let p1 = new Promise((resolve, reject) => {
  resolve("ok");
});
let p2 = Promise.resolve("hello");
let p3 = Promise.resolve("oh yeah");
let result = Promise.all([p1, p2, p3]);
console.log(result);

race

all方法类似都是接受一个Promise对象的数组,但其状态与结果由数组中最先改变的Promise对象决定。

1
2
3
4
5
6
7
8
9
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("ok");
  }, 2000);
});
let p2 = Promise.resolve("success");
let p3 = Promise.resolve("oh hou");
let result = Promise.race([p1, p2, p3]);
console.log(result);

allSettled

all方法类似,当数组内所有promise的状态都确定后执行成功的回调。基本上只有成功的回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function ajax(url) {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open("get", url, true);
    xhr.send();
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(xhr.responseText);
        } else {
          reject(xhr.responseText);
        }
      }
    };
  });
}

any

all方法正好相反,只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfiilled 状态;
如果所有参数实例都变成 rejected,包装实例就会变成 rejected 状态,结果为一个AggregateError对象。

1
2
3
4
5
6
7
8
9
10
let p1 = new Promise((resolve, reject) =>{" "}
{setTimeout(() => {
  resolve("ok");
}, 1000)}
) let p2 = new Promise((resolve, reject) => {setTimeout(() => {
  resolve("okk");
}, 2000)}) let p3 = new Promise((resolve, reject) => {setTimeout(() => {
  reject("error");
}, 3000)}) Promise.any([p1, p2, p3]).then(res => {console.log(res)}).catch(err
=> {console.log("error")})

关键问题

中断 Promise 链

终止Promise链条,有且仅有一种方式,即返回一个状态为pendingpromise对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
new Promise((resolve, reject) => {
  resolve(111);
})
  .then((value) => {
    console.log(value);
    console.log(222);
    //
    // return false;
    // throw '出错啦';
    //有且只有一种方式 返回一个pending状态的promise对象
    return new Promise((resolve, reject) => {});
  })
  .then((value) => {
    console.log(333);
  })
  .then((value) => {
    console.log(444);
  })
  .catch((reason) => {
    console.log(reason);
  });

修改 Promise 的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
//如何修改 promise 对象状态
let p = new Promise((resolve, reject) => {
  //1. resolve
  // resolve('success');
  //2. reject
  // reject('error');
  //3. 抛出错误 异常
  // throw '出问题啦! 你说出这样的话  你没有良心!!';
  // 状态的改变只有一次
  resolve("ok");
  reject("error");
});
console.log(p);

Promise 串联多个任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
new Promise((resolve, reject) => {
  console.log(111);
  reject();
})
  .then((value) => {
    console.log(222);
  })
  .then((value) => {
    console.log(value);
  })
  .then(
    (value) => {
      console.log(value);
    },
    (reason) => {
      console.error(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
setTimeout(() => {
  console.log("0");
}, 0);
new Promise((resolve, reject) => {
  console.log("1");
  resolve();
})
  .then(() => {
    console.log("2");
    new Promise((resolve, reject) => {
      console.log("3");
      resolve();
    })
      .then(() => {
        console.log("4");
      })
      .then(() => {
        console.log("5");
      });
  })
  .then(() => {
    console.log("6");
  });

new Promise((resolve, reject) => {
  console.log("7");
  resolve();
}).then(() => {
  console.log("8");
});

// 我们来分析一下这段代码执行结果的打印顺序,注意宏队列只会在微队列为空后才会执行
// 1. 同步执行:1 7
// 2. 异步队列中的宏队列与微队列出队列,进入执行栈执行:2 3 8
// 3. 4 比 6 先进微队列,:4 6
// 4. 5 出微队列后为空,宏队列开始执行:5 0

使用 JS 手写一个 promise

由于注释写的比较清除就不分步写了,注意Promise类的静态方法race等,接受的是一个可迭代对象,同时注意在写的时候所用到的技巧,foreach以及数组的原生方法。
值得注意的地方有:

  • _resolve的单独处理,处理嵌套的Promise对象。
  • 静态方法接受可迭代对象的方法大致相同,但注意空数组的时候返回什么。
  • 注意原生实现的时候then方法不能使用箭头函数定义,会出现this指向无法指向实例对象的问题。

原生实现

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
// 首先使用一个自执行函数,避免全局污染
((window) => {
  // 注意这里不能使用箭头函数,箭头函数不能用作构造函数
  function Promise(executor) {
    this.PromiseState = "pending";
    this.PromiseResult = undefined;
    // 定义储存回调函数的数组
    this.callbacks = [];
    const _resolve = (value) => {
      // 注意 Promise 的状态只能更改一次
      if (this.PromiseState !== "pending") return;
      // 这里需要对值是 promise 对象进行格外处理
      if (value instanceof Promise) {
        value.then(_resolve, _reject);
      } else {
        this.PromiseState = "fullfilled";
        this.PromiseResult = value;
        this.callbacks.forEach((callback) => {
          callback.onResolved();
        });
      }
    };
    const _reject = (reason) => {
      // 注意 Promise 的状态只能更改一次
      if (this.PromiseState !== "pending") return;
      this.PromiseState = "rejected";
      this.PromiseResult = reason;
      this.callbacks.forEach((callback) => {
        callback.onRejected();
      });
    };
    try {
      executor(_resolve, _reject);
    } catch (error) {
      _reject(error);
    }
  }
  Object.assign(Promise.prototype, {
    // 在这里我们需要明确,then 方法需要返回一个 Promise 对象,并且需要对 onResolved 与 onRejected 是否是一个函数
    then(onResolved, onRejected) {
      if (!(onResolved instanceof Function)) onResolved = (value) => value;
      if (!(onRejected instanceof Function))
        onRejected = (reason) => {
          throw reason;
        };
      return new Promise((resolve, reject) => {
        // 抽离统一处理的方法
        const __common = (callback) => {
          // 通过 setTimeout 模拟异步回调的方法
          setTimeout(() => {
            // 还需要考虑到出错的情况
            try {
              // 注意我们现在需要通过这个的返回结果获取 then 方法返回的 Promise 的状态
              const result = callback(this.PromiseResult);
              if (result instanceof Promise) {
                // 如果返回的是 Promise 对象,则状态由 Promise 对象决定,则将状态的改变手段,交予该对象的 then 的回调
                result.then(resolve, reject);
              } else {
                resolve(result);
              }
            } catch (error) {
              reject(error);
            }
          });
        };
        // 这里 then 方法返回了一个 Promise 对象,其状态应该是由 onResolved 或 onRejected 的执行结果决定的,这里我们首先先判断执行哪一个
        if (this.PromiseState === "fullfilled") {
          __common(onResolved);
        } else if (this.PromiseState === "rejected") {
          __common(onRejected);
        }
        // 同时,这里只考虑到,状态改变后执行回调的情况,没有考虑回调定义在状态改变之前的情况,这里我们来考虑 pending 的情况
        else if (this.PromiseState === "pending") {
          this.callbacks.push({
            onResolved: () => __common(onResolved),
            onRejected: () => __common(onRejected),
          });
        }
      });
    },
    catch(onRejected) {
      return this.then(undefined, onRejected);
    },
    finally(callback) {
      // finally 方法是无论如何都会执行并返回一个等效的 Promise 对象
      return this.then(
        (value) => {
          return Promise.resolve(callback()).then(() => value);
        },
        (reason) => {
          return Promise.resolve(callback()).then(() => {
            throw reason;
          });
        }
      );
    },
  });

  // 定义类身上的静态方法
  // 定义 resolve 方法
  Promise.resolve = (value) => {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) value.then(resolve, reject);
      else resolve(value);
    });
  };
  // 定义 reject 方法
  Promise.reject = (reason) => {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  };
  // 定义 all 方法
  Promise.all = (iterable) => {
    if (!iterable[Symbol.iterator]) {
      throw new TypeError("Argument must be iterable");
    }
    return new Promise((resolve, reject) => {
      // all 方法则为,当所有状态为 fullfilled 的才会返回成功,有 rejected 就会返回失败
      // 将可迭代对象转换为数组
      const promises = Array.from(iterable);
      // 如果数组为空,直接返回一个已成功的Promise对象,值为一个空数组
      if (promises.length === 0) {
        return resolve([]);
      }
      const length = promises.length;
      let arr = new Array(length);
      let count = 0;
      for (let i = 0; i < length; i++) {
        if (promises[i] instanceof Promise) {
          promises[i].then((value) => {
            arr[i] = value;
            count++;
            if (count === length) resolve(arr);
          }, reject);
        } else {
          arr[i] = promises[i];
          count++;
          if (count === length) resolve(arr);
        }
      }
    });
  };
  Promise.race = (iterable) => {
    if (!iterable[Symbol.iterator]) {
      throw new TypeError("Argument must be iterable");
    }
    return new Promise((resolve, reject) => {
      const promises = Array.from(iterable);
      if (promises.length === 0) {
        return;
      }
      for (let i = 0; i < promises.length; i++) {
        if (promises[i] instanceof Promise) {
          promises[i].then(resolve, reject);
        } else {
          resolve(promises[i]);
        }
      }
    });
  };
  // promise.allSettled方法
  Promise.allSettled = function (iterable) {
    // 参数必须是一个可迭代对象
    if (!iterable[Symbol.iterator]) {
      throw new TypeError("Argument must be iterable");
    }
    // 返回一个新的promise对象
    return new Promise((resolve, reject) => {
      // 将可迭代对象转换为数组
      const promises = Array.from(iterable);
      // 如果数组为空,直接返回一个已完成的promise,值为一个空数组
      if (promises.length === 0) {
        resolve([]);
        return;
      }
      // 定义一个结果数组,用来存放每个promise的状态和值或原因
      const results = new Array(promises.length);
      // 定义一个计数器,用来记录已完成的promise的数量
      let count = 0;
      // 遍历每个promise
      promises.forEach((promise, index) => {
        // 将非promise对象转换为promise对象
        Promise.resolve(promise)
          // 如果成功,将结果对象存入数组,并增加计数器
          .then((value) => {
            results[index] = { status: "fulfilled", value };
            count++;
            // 如果所有的promise都完成了,就返回一个已完成的promise,值为结果数组
            if (count === promises.length) {
              resolve(results);
            }
          })
          // 如果失败,同样将结果对象存入数组,并增加计数器
          .catch((reason) => {
            results[index] = { status: "rejected", reason };
            count++;
            // 如果所有的promise都完成了,就返回一个已完成的promise,值为结果数组
            if (count === promises.length) {
              resolve(results);
            }
          });
      });
    });
  };

  // promise.any方法
  Promise.any = function (iterable) {
    // 参数必须是一个可迭代对象
    if (!iterable[Symbol.iterator]) {
      throw new TypeError("Argument must be iterable");
    }
    // 返回一个新的promise对象
    return new Promise((resolve, reject) => {
      // 将可迭代对象转换为数组
      const promises = Array.from(iterable);
      // 如果数组为空,直接返回一个已拒绝的promise,原因为一个空的AggregateError
      if (promises.length === 0) {
        reject(new AggregateError([]));
        return;
      }
      // 定义一个错误数组,用来存放每个promise的失败原因
      const errors = new Array(promises.length);
      // 定义一个计数器,用来记录已拒绝的promise的数量
      let count = 0;
      // 遍历每个promise
      promises.forEach((promise, index) => {
        // 将非promise对象转换为promise对象
        Promise.resolve(promise)
          // 如果成功,直接返回一个已完成的promise,值为该promise的值
          .then((value) => {
            resolve(value);
          })
          // 如果失败,将失败原因存入错误数组,并增加计数器
          .catch((reason) => {
            errors[index] = reason;
            count++;
            // 如果所有的promise都拒绝了,就返回一个已拒绝的promise,原因为一个包含错误数组的AggregateError
            if (count === promises.length) {
              reject(new AggregateError(errors));
            }
          });
      });
    });
  };
  // 使用自定义的 Promise 替换掉默认的 Promise
  window.Promise = Promise;
})(window);

class 实现

原生的都写出来了只能说class版本有手就行,注意class field写法的方法仍然属于实例属性,class field的主要设计目的就是为了解决在类里面使用this的种种不便,精简了部分代码。

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
class Promise {
  constructor(executor) {
    this.PromiseState = "pending";
    this.PromiseResult = undefined;
    this.callbacks = [];
    const _resolve = (value) => {
      if (this.PromiseState !== "pending") return;
      if (value instanceof Promise) value.then(_resolve, _reject);
      else {
        this.PromiseState = "fullfilled";
        this.PromiseResult = value;
        this.callbacks.forEach((callback) => callback.onResolved());
      }
    };
    const _reject = (reason) => {
      if (this.PromiseState !== "pending") return;
      this.PromiseState = "rejected";
      this.PromiseResult = reason;
      this.callbacks.forEach((callback) => callback.onRejected());
    };
    try {
      executor(_resolve, _reject);
    } catch (error) {
      _reject(error);
    }
  }
  then(onResolved, onRejected) {
    if (!(onResolved instanceof Function)) onResolved = (value) => value;
    if (!(onRejected instanceof Function))
      onRejected = (reason) => {
        throw reason;
      };
    return new Promise((resolve, reject) => {
      const _common = (callback) => {
        setTimeout(() => {
          try {
            const result = callback(this.PromiseResult);
            if (result instanceof Promise) result.then(resolve, reject);
            else resolve(result);
          } catch (error) {
            reject(error);
          }
        });
      };
      if (this.PromiseState === "fullfilled") {
        _common(onResolved);
      } else if (this.PromiseState === "rejected") {
        _common(onRejected);
      } else if (this.PromiseState === "pending") {
        this.callbacks.push({
          onResolved: () => _common(onResolved),
          onRejected: () => _common(onRejected),
        });
      }
    });
  }
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
  finally(callback) {
    return this.then(
      (value) => Promise.resolve(callback()).then(() => value),
      (reason) =>
        Promise.resolve(callback()).then(() => {
          throw reason;
        })
    );
  }
  static resolve(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) value.then(resolve, reject);
      else {
        resolve(value);
      }
    });
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }
  static all(iterables) {
    if (!iterables[Symbol.iterator]) {
      throw new TypeError(
        `TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
      );
    }
    return new Promise((resolve, reject) => {
      const promises = Array.from(iterables);
      if (promises.length === 0) return resolve([]);
      const results = new Array(promises.length);
      let count = 0;
      promises.forEach((promise, index) => {
        Promise.resolve(promise).then((value) => {
          results[index] = value;
          count++;
          if (count === promises.length) resolve(results);
        }, reject);
      });
    });
  }
  static race(iterables) {
    if (!iterables[Symbol.iterator]) {
      throw new TypeError(
        `TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
      );
    }
    return new Promise((resolve, reject) => {
      const promises = Array.from(iterables);
      promises.forEach((promise) => {
        Promise.resolve(promise).then(resolve, reject);
      });
    });
  }
  static allSettled(iterables) {
    if (!iterables[Symbol.iterator]) {
      throw new TypeError(
        `TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
      );
    }
    return new Promise((resolve, reject) => {
      const promises = Array.from(iterables);
      if (promises.length === 0) return resolve([]);
      const results = new Array(promises.length);
      let count = 0;
      promises.forEach((promise, index) => {
        Promise.resolve(promise).then(
          (value) => {
            results[index] = { status: "fullfilled", value };
            count++;
            if (count === promises.length) resolve(results);
          },
          (reason) => {
            results[index] = { status: "rejected", reason };
            count++;
            if (count === promises.length) resolve(results);
          }
        );
      });
    });
  }
  static any(iterables) {
    if (!iterables[Symbol.iterator]) {
      throw new TypeError(
        `TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
      );
    }
    return new Promise((resolve, reject) => {
      const promises = Array.from(iterables);
      if (promises.length === 0) return reject(new AggregateError([]));
      const results = new Array(promises.length);
      let count = 0;
      promises.forEach((promise, index) => {
        Promise.resolve(promise).then(resolve, (reason) => {
          results[index] = reason;
          count++;
          if (count === promises.length) reject(new AggregateError(results));
        });
      });
    });
  }
}