为叙述方便,以下代码全部将fulfilled
状态改为resolved
状态。
1 简易版
js
function Promise(executor) {
let self = this;
self.status = "pending"; // 定义状态改变前的初始状态
self.value = undefined; // 定义状态为resolved的时候的状态
self.reason = undefined; //定义状态为rejected的时候的状态
function resolve(value) {
// 将状态由 pending --> resolved
if (self.status === "pending") {
self.value = value;
self.status = "resolved";
}
}
function reject(reason) {
// 将状态由 pending --> rejected
if (self.status === "pending") {
self.reason = reason;
self.status = "rejected";
}
}
//捕获构造异常
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 定义链式调用的then方法
Promise.prototype.then = function (onResolved, onRejected) {
let self = this;
// 校验 onResolved 和 onRejected
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
// prettier-ignore
onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err; };
switch (self.status) {
case "resolved":
onResolved(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
break;
}
};
缺陷
-
简易版只能处理同步的情况,在
new Promise()
构造函数里,如果有异步调用,则无法处理。jsvar p = new Promise((resolve, reject) => { console.log(1); setTimeout(() => { resolve("异步"); }); }); p.then((x) => { console.log(x); }); // 最终只打印1,没有打印异步
2 修复构造函数中异步处理
问题原因
由于构造函数里的resolve
是异步,因此p.then
的回调要更先执行,而此时的Promise
的状态还是pending
(resolve
是异步的,还没执行),因此then
方法里的case
匹配失败。
解决方案
- 修改
then
方法,pending
状态时收集回调函数。 resolve
或者reject
时,遍历执行pending
状态时收集的回调函数。
js
function Promise(executor) {
let self = this;
self.status = "pending"; // 定义状态改变前的初始状态
self.value = undefined; // 定义状态为resolved的时候的状态
self.reason = undefined; //定义状态为rejected的时候的状态
self.onResolvedCallbacks = []; // 1.1:定义pending状态下需存放的resolved回调函数
self.onRejectedCallbacks = []; // 1.2:定义pending状态下需存放的rejected回调函数
function resolve(value) {
// 将状态由 pending --> resolved
if (self.status === "pending") {
self.value = value;
self.status = "resolved";
// 2.1: 一旦 resolve 执行,遍历执行已收集到的resolved回调函数
self.onResolvedCallbacks.forEach((fn) => fn());
}
}
function reject(reason) {
// 将状态由 pending --> rejected
if (self.status === "pending") {
self.reason = reason;
self.status = "rejected";
// 2.2: 同样,一旦 reject 执行,遍历执行已收集到的rejected回调函数
self.onRejectedCallbacks.forEach((fn) => fn());
}
}
//捕获构造异常
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 定义链式调用的then方法
Promise.prototype.then = function (onResolved, onRejected) {
let self = this;
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
// prettier-ignore
onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err; };
switch (self.status) {
case "resolved":
onResolved(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
case "pending":
// 1.3: pending状态时收集当前onResolved方法
self.onResolvedCallbacks.push(() => {
onResolved(self.value);
});
// 1.4: pending状态时同样需要收集当前onRejected方法
self.onRejectedCallbacks.push(() => {
onRejected(self.reason);
});
default:
break;
}
};
执行以下例子:
js
const p = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
resolve("异步");
});
});
p.then((x) => {
console.log(x);
});
// 1
// 异步
再次执行上述例子,会发现已经能打印1
和异步
。
缺陷
-
无法链式调用
jsconst p = new Promise((resolve, reject) => { resolve(1); }); p.then((x) =>{ console.log(x); }).then(() => { console.log(2) }); // 报错
3 修复链式调用
问题原因
若要链式调用then
方法的话,需要then
返回一个Promise
对象,而我们前面这版并没有做到这点,故无法链式调用then
方法。
解决方案
-
在
then
方法中返回一个新的Promise
(称为P1), P1的状态由回调函数决定。- 如果回调函数返回的结果是一个
Promise
对象(称为P2),那么P1 的状态将与P2 对象的状态相同。 - 如果回调函数返回的结果不是一个
Promise
对象,则用返回的结果resolve
掉P1。 - 如果回调函数抛出错误,那么P1的状态将为
rejected
,用try catch
捕获错误达到将P1状态改为rejected
。
- 如果回调函数返回的结果是一个
js
function Promise(executor) {
// ...上一版的Promise构造函数代码
}
Promise.prototype.then = function (onResolved, onRejected) {
let self = this;
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err; };
// 1.1 返回新的Promise对象(称为P1)
return new Promise((resolve, reject) => {
const callback = (fn) => {
try {
// 执行then方法传入的onResolved或者onRejected回调,得到结果result
const result = fn(self.value || self.reason);
if (result instanceof Promise) {
// 1.2 返回的结果是一个 Promise 对象(称为P2),那么P1 的状态将与P2 对象的状态相同。
// prettier-ignore
result.then((res) => resolve(res), (err)=> reject(err))
}
// 1.3 返回的结果不是一个Promise对象,则用返回的结果resolve掉P1。
resolve(result);
} catch (err) {
// 1.4 报错则用报错信息reject掉P1
reject(err);
}
};
switch (self.status) {
case "resolved":
callback(onResolved);
break;
case "rejected":
callback(onRejected);
break;
case "pending":
self.onResolvedCallbacks.push(() => { callback(onResolved); });
self.onRejectedCallbacks.push(() => { callback(onRejected); });
default:
break;
}
});
};
再次执行以下例子:
js
const p = new Promise((resolve, reject) => {
resolve(1);
});
p.then((x) =>{
console.log(x);
}).then(() => {
console.log(2)
});
// 1
// 2
已成功打印出1和2,说明我们的then
链式调用改造成功。
至此,一个简易版的Promise已经实现,当然还有很多边界情况我们没有考虑:如
then
方法里返回的如果是具有then方法的对象或者函数、那么也是可以当做一个Promise来对待的。then
方法是异步的。这点需要通过setTimeout
来优化我们的then
方法。- 还有其他情况等等。
4 catch race all方法
实现了Promise
的then
方法,那么其他方法就比较简单了,如catch
方法其实是一个没有第一个入参的then
方法。
catch :Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数
js
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
测试:
js
const p = new Promise((resolve, reject) => {
reject("err");
});
p.catch((res) => {
console.log(res);
});
// 输出:err
race :race
方法将多个 Promise
实例,包装成一个新的 Promise
实例。率先改变状态的Promise实例,其返回值将传回给回调函数。
js
Promise.prototype.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, reject);
});
});
};
测试:
js
const p = new Promise((resolve, reject) => {
resolve();
});
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1");
}, 300);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2");
}, 200);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3");
}, 100);
});
p.race([p1, p2, p3]).then((res) => {
console.log(res);
});
// 输出:p3
all : all
方法同样将多个 Promise
实例,包装成一个新的 Promise
实例。 只不过是需要所有的Promise
实例的状态都resolved
后,其返回值组成的数组才传回给回调函数;或最先rejected
的Promise
实例,其返回值传给回调函数。
js
Promise.prototype.all = function (promises) {
const result = [];
let resolvedCount = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(res) => {
// 这里不能用push,因为要保证顺序
result[i] = res;
resolvedCount += 1;
// 这里不能用result.length去判等,因为result不是用push保存的
if (resolvedCount === promises.length) {
resolve(result);
}
},
(err) => reject(err)
);
}
});
};
测试:
js
const p = new Promise((resolve, reject) => {
resolve();
});
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1");
}, 300);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2");
}, 200);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3");
}, 100);
});
p.all([p1, p2, p3]).then((res) => {
console.log(res);
});
// 输出:[ 'p1', 'p2', 'p3' ]
5 完整代码
js
function Promise(executor) {
let self = this;
self.status = "pending"; // 定义状态改变前的初始状态
self.value = undefined; // 定义状态为resolved的时候的状态
self.reason = undefined; //定义状态为rejected的时候的状态
self.onResolvedCallbacks = []; // 1.1:定义pending状态下需存放的resolved回调函数
self.onRejectedCallbacks = []; // 1.2:定义pending状态下需存放的rejected回调函数
function resolve(value) {
// 将状态由 pending --> resolved
if (self.status === "pending") {
self.value = value;
self.status = "resolved";
// 2.1: 一旦 resolve 执行,遍历执行已收集到的resolved回调函数
self.onResolvedCallbacks.forEach((fn) => fn());
}
}
function reject(reason) {
// 将状态由 pending --> rejected
if (self.status === "pending") {
self.reason = reason;
self.status = "rejected";
// 2.2: 同样,一旦 reject 执行,遍历执行已收集到的rejected回调函数
self.onRejectedCallbacks.forEach((fn) => fn());
}
}
//捕获构造异常
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function (onResolved, onRejected) {
let self = this;
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
// prettier-ignore
onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err; };
// 1.1 返回新的Promise对象
return new Promise((resolve, reject) => {
const callback = (fn) => {
try {
// 执行then方法传入的onResolved或者onRejected回调,得到结果result
const result = fn(self.value || self.reason);
if (result instanceof Promise) {
// 1.2 返回的结果是一个 Promise 对象(称为P2),那么P1 的状态将与P2 对象的状态相同。
// prettier-ignore
result.then((res) => resolve(res), (err)=> reject(err))
}
// 1.3 返回的结果不是一个Promise对象,则用返回的结果resolve掉P1。
resolve(result);
} catch (err) {
// 1.4 报错则用报错信息reject掉P1
reject(err);
}
};
switch (self.status) {
case "resolved":
callback(onResolved);
break;
case "rejected":
callback(onRejected);
break;
case "pending":
// prettier-ignore
self.onResolvedCallbacks.push(() => { callback(onResolved); });
// prettier-ignore
self.onRejectedCallbacks.push(() => { callback(onRejected); });
default:
break;
}
});
};
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, reject);
});
});
};
Promise.prototype.all = function (promises) {
const result = [];
let resolvedCount = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(res) => {
// 这里不能用push,因为要保证顺序
result[i] = res;
resolvedCount += 1;
// 这里不能用result.length去判等,因为result不是用push保存的
if (resolvedCount === promises.length) {
resolve(result);
}
},
(err) => reject(err)
);
}
});
};
如有错误,欢迎大家指出~(抱拳啦!)