跳到主要内容

实现

一、Promise


1.1 状态特性

Promise 中有三种状态分别为: 等待Pending、成功Fulfilled、失败Rejected。状态的修改只能是 pendingrejected 或者 pendingresolved,且状态是不可逆的。

  • Pending --> Fulfilled
  • Pending --> Rejected

一旦状态改变不可更改

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

1.2 状态变更

Promise 的状态可以通过以下三种方式变更:

  • resolve(): 将PromisePending状态置为Fulfilled

  • reject(): 用于将PromisePending状态置为Rejected

  • throw(): 用于将PromisePending状态置为Rejected

二、Promise.constructor


2.1 执行器

Promise 是一个类,在实例化这个类的时候,需要传递一个回调函数进去,回调函数也成为执行器 。 new Promise()有一个回调函数需要实现,且回调函数需要有两个参数,所以构造函数需要有一个参数executor ,之后执行器(executor) 会立即执行

语法

class Promise{
constructor(executor){
executor();
}
}

三、Promise.prototype.resolve


resolve 用于将PromisePending状态置为Fulfilled

实现

resolve = (value) => {
if (this.status !== PENDING) {
return;
}
this.status = FULFILLED;
this.value = value;
this.fulfilledCallback.forEach((callback) => {
callback();
});
};

注意: 由于resolvePromise类内部调用,resolve中的this必须要指向Promise实例。所以最好使用箭头函数,避免引起this指向的问题。

四、Promise.prototype.reject


reject 用于将PromisePending状态置为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 方法上注册的回 调 onFulfilledonRejected 能够异步执行,且应该在 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 的异常穿透有以下特点:

  1. Promise 回调函数内部任何操作出现问题, 不会传递到外层代码, 不会退出进程, 不会中断外部程序执行

  2. Promise 失败回调 获取不到自身 成功回调 抛出的错误, 因此, 我们可以在最后指定失败的回调

  3. 前面任何操作出现了异常, 都会直接传递到最后失败的回调中进行处理。会跳过 抛出错误失败回调 的中间环节。

  4. 在失败的回调处理完成后, 返回的还是 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;
})
);
};

注意: 由于rejectPromise类内部调用,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 对象, 该实例的状态为rejectedPromise.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

  • valuePromise 实例: 那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

  • valuethenable 对象: 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,包含了fulfilledrejected两种情况。接受一个数组作为参数,数组的每个成员都是一个 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 install promises-aplus-tests -S

3. package.json配置命令

"scripts": {
"test":"promises-aplus-tests ./index.js"
}

4. 运行yarn test即可

参考资料