实现
一、Promise
1.1 状态特性
Promise 中有三种状态分别为: 等待Pending、成功Fulfilled、失败Rejected。状态的修改只能是 pending
到 rejected
或者 pending
到 resolved
,且状态是不可逆的。
Pending
-->Fulfilled
Pending
-->Rejected
一旦状态改变不可更改
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
1.2 状态变更
Promise
的状态可以通过以下三种方式变更:
-
resolve()
: 将Promise的Pending
状态置为Fulfilled
-
reject()
: 用于将Promise的Pending
状态置为Rejected
-
throw()
: 用于将Promise的Pending
状态置为Rejected
二、Promise.constructor
2.1 执行器
Promise 是一个类,在实例化这个类的时候,需要传递一个回调函数进去,回调函数也成为执行器 。 new Promise()
有一个回调函数需要实现,且回调函数需要有两个参数,所以构造函数需要有一个参数executor
,之后执行器(executor)
会立即执行
语法
class Promise{
constructor(executor){
executor();
}
}
三、Promise.prototype.resolve
resolve 用于将Promise的Pending
状态置为Fulfilled
实现
resolve = (value) => {
if (this.status !== PENDING) {
return;
}
this.status = FULFILLED;
this.value = value;
this.fulfilledCallback.forEach((callback) => {
callback();
});
};
注意: 由于resolve
是Promise
类内部调用,resolve
中的this
必须要指向Promise
实例。所以最好使用箭头函数,避免引起this指向的问题。
四、Promise.prototype.reject
reject 用于将Promise的Pending
状态置为Rejected
reject = (reason) => {
if (this.status !== PENDING) {
return;
}
this.status = REJECTED;
this.reason = reason;
this.rejectedCallback.forEach((callback) => {
callback();
});
};
五、Promise.prototype.then
Promise.prototype.then
创建新的Promise实例对象并返回
5.1 思路
回调穿透: 如果 then
方法中没有成功 / 失败的回调函数的话, 让 Promise
的值一直被传递下去
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
链式调用: 返回一个 Promise
对象, 让 Promise
可以链式调用
return new Promise()
Promise
执行器: 执行的工作如下:
-
**旧的
Promise
在执行器中同步调用resolve
,此时旧的Promise
状态为成功调用成功的回调函数,判断回调函数的返回值。执行Promise
**的解决过程 -
旧的
Promise
在执行器中同步调用reject
,此时**旧的Promise
状态为失败调用失败的回调函数,判断回调函数的返回值。执行Promise
**的解决过程 -
旧的
Promise
在执行器中异步调用resolve
或者reject
,此时**旧的Promise
状态为等待,则存储成功与失败回调函数。执行Promise
**的解决过程
为什么触发回调函数要放到setTimeout
里面呢?
答: 使用 setTimeout
执行回调函数是为了确保 then
方法上注册的回
调 onFulfilled
和 onRejected
能够异步执行,且应该在 then
方法被调用的那一
轮事件循环之后的新执行栈中执行。
5.2 实现
then(onFulfilled, onRejected) {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
const newPromise = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const value = onFulfilled(this.value);
resolution(newPromise, value, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const reason = onRejected(this.reason);
resolution(newPromise, reason, resolve, reject);
} catch (error) {
reject(error);
}
});
} else {
this.fulfilledCallback.push(() => {
setTimeout(() => {
try {
const value = onFulfilled(this.value);
resolution(newPromise, value, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.rejectedCallback.push(() => {
setTimeout(() => {
try {
const reason = onRejected(this.reason);
resolution(newPromise, reason, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
});
return newPromise;
}
5.3 问题
问题一、为什么需要重新创建一个Promise
实例对象呢?返回自身的Promise
对象(this
)不可以吗?
答: 返回一个新的Promise对象而不是返回自身,这样是为了防止循环引用。
问题二、为什么then
方法不像jQuery
那样返回this
而是要重新返回一个新的promise
对象?
答: 如果then
返回了this
,那么promise2
就和promise1
的状态同步,promise1
状态变更后,promise2
就没办法接受后面异步操作进行的状态变更
六、Promise.prototype.catch
6.1 思路
异常穿透return new Promise(()=>{})
** 以及 Promise.then(()=>{})
回调函数内部发生的错误具有 冒泡 的性质, 会一直向后传递,直到被捕获为止。也就是说: 错误总是被下一个 catch
语句捕获。因此, Promise
的异常穿透有以下特点:
-
Promise
回调函数内部任何操作出现问题, 不会传递到外层代码, 不会退出进程, 不会中断外部程序执行 -
Promise
失败回调 获取不到自身 成功回调 抛出的错误, 因此, 我们可以在最后指定失败的回调 -
前面任何操作出现了异常, 都会直接传递到最后失败的回调中进行处理。会跳过 抛出错误 到 失败回调 的中间环节。
-
在失败的回调处理完成后, 返回的还是
Promise
, 因此可以在失败的回调后继续.then
或者.catch
。
6.2 实现
catch(onRejected) {
return this.then(undefined, onRejected);
}
七、Promise.prototype.then resolution
function resolution(promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError("promise 相同,造成了循环引用"));
}
let called;
if ((typeof x === "object" && x !== null) || typeof x === "function") {
try {
const then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) {
return;
}
called = true;
resolution(promise, y, resolve, reject);
},
(reason) => {
if (called) {
return;
}
called = true;
reject(reason);
}
);
} else {
resolve(x);
}
} catch (error) {
if (called) {
return;
}
called = true;
reject(error);
}
} else {
resolve(x);
}
}
八、Promise.prototype.finally
Promise.prototype.finally
用于指定 Promise
对象变更状态之后执行的操作。无论是 Promise
最后的状态变为 fulfilled
还是 rejected
。 这避免了同样的语句需要在**then()和catch()**中各写一次的情况。最后返回一个新的 Promise
对象
8.1 实现
Promise
类中实现
finally(callback) {
return this.then(
(value) => {
return Promise.resolve(callback()).then(() => value);
},
(reason) => {
return Promise.reject(callback()).then(() => {
throw reason;
});
}
);
}
Promise
类外实现
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
(value) => promise.resolve(callback()).then(() => value),
(error) =>
promise.resolve(callback()).then(() => {
throw error;
})
);
};
注意: 由于reject
是Promise
类内部调用,reject
中的this
必须要指向Promise
实例,所以最好使用箭头函数,避免引起this指向的问题。
九、Promise.all
Promise.all
方法用于将多个 Promise
实例(如果某一个元素不是 Promise
实例, 会调用 Promise.resolve
将该元素转化为 Promise
实例。
),包装成一个新的 Promise
实例。 新的 Promise
实例的状态由传入的多个 Promise
实例的状态 决定。分两种情况:
-
传入的多个
Promise
实例状态都变为Fulfilled
: 新的Promise
实例的状态变为Fulfilled
。此时, 多个Promise
实例的返回值组成一个数组,传递给新的Promise
实例的回调函数 -
传入的多个
Promise
实例状态只要有一个变为Rejected
: 新的Promise
实例的状态变为Reject
, 此时第一个被Reject
的实例的返回值,会传递给新的Promise
实例的回调函数。
9.1 实现
Promise
类中实现
static all(promises) {
return new Promise((resolve, reject) => {
const result = [];
let count = 0;
function addResult(key, value) {
result[key] = value;
count++;
if (count === promises.length) {
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
addResult(i, value);
},
(reason) => {
reject(reason);
}
);
} else {
addResult(i, current);
}
}
});
}
十、Promise.any
Promise.any
方法用于将多个 Promise
实例(如果某一个元素不是 Promise
实例, 会调用 Promise.resolve
将该元素转化为 Promise
实例。
),包装成一个新的 Promise
实例。 只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。抛出的错误是一个 AggregateError
实例, 这个 AggregateError
实例对象的errors
属性是一个数组,包含了所有成员的错误。
10.1 实现
Promise
类中实现
static any(promises) {
return new Promise((resolve, reject) => {
let count = 0;
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
resolve(value);
},
(reason) => {
count++;
if (count.length === promise.length) {
reject(new AggregateError("All promises were rejected"));
}
}
);
} else {
resolve(current);
}
}
});
}
十一、Promise.race
Promise.race
方法用于将多个 Promise
实例(如果某一个元素不是 Promise
实例, 会调用 Promise.resolve
将该元素转化为 Promise
实例。
),包装成一个新的 Promise
实例。 新的 Promise
实例的状态同多个 Promise
实例中率先改变的 Promise
实例状态一致。那个率先改变的 Promise
实例的返回值,就传递给的新的 Promise
实例的回调函数。
11.1 实现
Promise
类中实现
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
resolve(value);
return;
},
(reason) => {
reject(reason);
return;
}
);
} else {
resolve(current);
return;
}
}
});
}
十二、Promise.reject
Promise.reject
将现有对象转为 Promise
对象, 该实例的状态为rejected
。Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数。所以, Promise.reject
并不像 Promise.resolve
那样复杂。
12.1 实现
Promise
类中实现
static reject(reason){
if (reason instanceof Promise) {
return reason;
}
return new Promise((resolve,reject)=>reject(reason));
}
十三、Promise.resolve
Promise.resolve
将现有对象转为 Promise
对象。其中, Promise.resolve(value)
value
的情况如下:
-
value
为空: 直接返回一个resolved
状态的Promise
对象。 -
value
为 普通数据: 返回一个新的Promise
对象,状态为resolved
。 -
value
为Promise
实例: 那么Promise.resolve
将不做任何修改、原封不动地返回这个实例。 -
value
为thenable
对象:Promise.resolve()
方法会将这个对象转为Promise
对象,然后就立即执行thenable
对象的then()
方法。
13.1 实现
Promise
类中实现
static resolve(value) {
if (value instanceof Promise) {
return value;
}
return new Promise((resolve) => {
resolve(value);
});
}
十四、Promise.allSettled
Promise.allSettled
用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做Settled
,包含了fulfilled
和rejected
两种情况。接受一个数组作为参数,数组的每个成员都是一个 Promise
对象,并返回一个新的 Promise
对象。只有等到参数数组的所有 Promise
对象都发生状态变更(不管是fulfilled
还是rejected
),返回的 Promise
对象才会发生状态变更。并且, Promise.allSettled
只有 penging -> fulfilled
的状态变化。状态只可能变成fulfilled
。
14.1 实现
Promise
类中实现
static allSettled(promises) {
return new Promise((resolve, reject) => {
const result = [];
let count = 0;
function addResult(key, status, value) {
const obj = { status };
if (status === "fulfilled") {
obj.value = value;
} else {
obj.reason = value;
}
result[key] = obj;
count++;
if (count === promises.length) {
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
addResult(i, "fulfilled", value);
},
(reason) => {
addResult(i, "rejected", reason);
}
);
} else {
addResult(i, "fulfilled", current);
}
}
});
}
十五、完整代码
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise {
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallback = [];
rejectedCallback = [];
constructor(executor) {
try {
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
resolve = (value) => {
if (this.status !== PENDING) {
return;
}
this.status = FULFILLED;
this.value = value;
this.fulfilledCallback.forEach((callback) => {
callback();
});
};
reject = (reason) => {
if (this.status !== PENDING) {
return;
}
this.status = REJECTED;
this.reason = reason;
this.rejectedCallback.forEach((callback) => {
callback();
});
};
then(onFulfilled, onRejected) {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
const newPromise = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const value = onFulfilled(this.value);
resolution(newPromise, value, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const reason = onRejected(this.reason);
resolution(newPromise, reason, resolve, reject);
} catch (error) {
reject(error);
}
});
} else {
this.fulfilledCallback.push(() => {
setTimeout(() => {
try {
const value = onFulfilled(this.value);
resolution(newPromise, value, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.rejectedCallback.push(() => {
setTimeout(() => {
try {
const reason = onRejected(this.reason);
resolution(newPromise, reason, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
});
return newPromise;
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
finally(callback) {
return this.then(
(value) => {
return Promise.resolve(callback()).then(() => value);
},
(reason) => {
return Promise.reject(callback()).then(() => {
throw reason;
});
}
);
}
static defer() {
const dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
static deferred() {
const dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
static resolve(value) {
if (value instanceof Promise) {
return value;
}
return new Promise((resolve) => {
resolve(value);
});
}
static reject(reason) {
if (reason instanceof Promise) {
return reason;
}
return new Promise((resolve, reject) => {
reject(reason);
});
}
static all(promises) {
return new Promise((resolve, reject) => {
const result = [];
let count = 0;
function addResult(key, value) {
result[key] = value;
count++;
if (count === promises.length) {
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
addResult(i, value);
},
(reason) => {
reject(reason);
}
);
} else {
addResult(i, current);
}
}
});
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
resolve(value);
},
(reason) => {
reject(reason);
}
);
} else {
resolve(current);
}
}
});
}
static any(promises) {
return new Promise((resolve, reject) => {
let count = 0;
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
resolve(value);
},
(reason) => {
count++;
if (count.length === promise.length) {
reject(new AggregateError("All promises were rejected"));
}
}
);
} else {
resolve(current);
}
}
});
}
static allSettled(promises) {
return new Promise((resolve, reject) => {
const result = [];
let count = 0;
function addResult(key, status, value) {
const obj = { status };
if (status === "fulfilled") {
obj.value = value;
} else {
obj.reason = value;
}
result[key] = obj;
count++;
if (count === promises.length) {
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (current instanceof Promise) {
current.then(
(value) => {
addResult(i, "fulfilled", value);
},
(reason) => {
addResult(i, "rejected", reason);
}
);
} else {
addResult(i, "fulfilled", current);
}
}
});
}
}
function resolution(promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError("promise 相同,造成了循环引用"));
}
let called;
if ((typeof x === "object" && x !== null) || typeof x === "function") {
try {
const then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) {
return;
}
called = true;
resolution(promise, y, resolve, reject);
},
(reason) => {
if (called) {
return;
}
called = true;
reject(reason);
}
);
} else {
resolve(x);
}
} catch (error) {
if (called) {
return;
}
called = true;
reject(error);
}
} else {
resolve(x);
}
}
module.exports = Promise;
十六、测试用例
function testFinally() {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100);
}, 200);
});
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
console.log("哈哈哈");
});
}
testFinally();
function testAll() {
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "foo");
});
Promise.all([p1, p2, p3]).then((values) => {
console.log(values); // [3, 1337, "foo"]
});
}
testAll();
function testAny() {
const pErr = new Promise((resolve, reject) => {
reject("总是失败");
});
const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最终完成");
});
const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
// pFast fulfils first
});
// 期望输出: "很快完成"
}
testAny();
function testRace() {
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then(function (value) {
console.log(value); // "two"
// 两个都完成,但 p2 更快
});
var p3 = new Promise(function (resolve, reject) {
setTimeout(resolve, 100, "three");
});
var p4 = new Promise(function (resolve, reject) {
setTimeout(reject, 500, "four");
});
Promise.race([p3, p4]).then(
function (value) {
console.log(value); // "three"
// p3 更快,所以它完成了
},
function (reason) {
// 未被调用
}
);
var p5 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, "five");
});
var p6 = new Promise(function (resolve, reject) {
setTimeout(reject, 100, "six");
});
Promise.race([p5, p6]).then(
function (value) {
// 未被调用
},
function (reason) {
console.log(reason); // "six"
// p6 更快,所以它失败了
}
);
}
testRace();
function testAllSettled() {
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
setTimeout(reject, 100, "foo")
);
const promises = [promise1, promise2];
Promise.allSettled(promises).then(
(results) => results.forEach((result) => console.log(result.status)) //// "fulfilled"
// "rejected"
);
}
testAllSettled();
十七、测试 PromiseA+ 规范
Promise写完之后可以通过promises-aplus-tests这个包对我们写的代码进行测试,看是否符合**A+**规范。
1. Promise 添加测试方法
static defer() {
const dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
static deferred() {
const dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
2. 安装promises-aplus-tests
依赖
- NPM
- Yarn
npm install promises-aplus-tests -S
yarn add promises-aplus-tests -S
3. package.json
配置命令
"scripts": {
"test":"promises-aplus-tests ./index.js"
}
4. 运行yarn test
即可