今天笔者不再讲promise
使用方面的知识了,小伙伴们可以自行查阅相关文档,或者看笔者前面写的js异步编程。今天我们的重点是自己动手实现一个promise
,并且完全符合Promise A+规范
。
好啦,咱们开始吧。
基础版
我们知道promise
的构造方法接收一个executor(resolve, reject)
函数,在new Promise()
时就会立刻执行这个executor
回调。
并且对于then
里面的回调方法,不是立即执行的,而是异步执行的。属于微任务。所以下面我们使用两个队列来分别存储各自回调函数。
class MyPromise {
// 定义两个数组,存储回调
resolveQueue = [];
rejectQueue = [];
constructor(executor) {
// 实现resolve
const resolve = (val) => {
if (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(val);
}
};
// 实现reject
const reject = (val) => {
if (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(val);
}
};
// new Promise()时立即执行executor,并传入resolve和reject
executor(resolve, reject);
}
// 存储回调
then(resolveFun, rejectFun) {
this.resolveQueue.push(resolveFun);
this.rejectQueue.push(rejectFun);
}
}
测试,先输出同步代码
然后再输出 success
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
mp1.then((res) => {
console.log(res); // success
});
console.log("同步代码");
到这我们基础版的Promise
就实现好了,但是它是不符合Promise A+
规范的。
那什么是Promise A+规范呢?
Promise A+规范
Promise/A+
的规范比较长,这里只总结两条核心规则:
Promise
本质是一个状态机,且状态只能为以下三种:Pending(等待态)
、Fulfilled(执行态)
、Rejected(拒绝态)
,状态的变更是单向的,只能从Pending -> Fulfilled
或Pending -> Rejected
,状态变更不可逆then方法
接收两个可选参数,分别对应状态改变时触发的回调。then
方法可以被同一个promise
调用多次。then
方法返回一个promise
可以链式调用。
对于第一条,是肯定不符合的,因为我们写的promise
里面根本就没定义这三种状态。
对于第二条,同一个promise
能被调用多次,我们来测试一下。
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
mp1.then((res) => {
console.log(res);
});
mp1.then((res) => {
console.log(res);
});
对于同一个promise
可以被多次调用,发现我们的promise
只能执行一次。😢
对于then
方法返回一个promise
可以链式调用。我们来测试一下。
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
mp1
.then((res) => {
console.log(res);
})
.then((res) => {
console.log(res);
});
居然直接报错了。😦
好吧,接下来我们根据规范,来完善一下我们上面的代码。
首先我们来实现三种状态
实现三种状态
// 定义Promise/A+规范的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
// 定义两个数组,存储回调
resolveQueue = [];
rejectQueue = [];
status = PENDING;
constructor(executor) {
// 实现resolve
const resolve = (val) => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = FULFILLED;
// 执行回调
if (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(val);
}
};
// 实现reject
const reject = (val) => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = REJECTED;
// 执行回调
if (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(val);
}
};
// new Promise()时立即执行executor,并传入resolve和reject函数供外部使用
executor(resolve, reject);
}
// 存储回调
then(resolveFun, rejectFun) {
this.resolveQueue.push(resolveFun);
this.rejectQueue.push(rejectFun);
}
}
这样我们的promise
就有三种状态啦。😀
实现多次调用
想要实现多次调用其实改一个地方就可以了。只需要将构造函数里我们执行回调的地方if
判断改成while
就可以了。因为if
只会调用一次,只会取回调数组中的第一个回调执行,而while
可以将整个回调数组清空。
// ...
constructor(executor) {
// 实现resolve
const resolve = (val) => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = FULFILLED;
while (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(val);
}
};
// 实现reject
const reject = (val) => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = REJECTED;
while (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(val);
}
};
// new Promise()时立即执行executor,并传入resolve和reject
executor(resolve, reject);
}
// ...
我们来测试下
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
mp1.then((res) => {
console.log(res);
});
mp1.then((res) => {
console.log(res);
});
这下就可以啦,调用几次执行几次。😀
实现链式调用
要想实现链式调用,那肯定是then()
方法要返回一个promise
,因为只有promise
对象才会有then()
方法。并且,下一个then
能获取到上一个then
的返回值。
// ...
then(resolveFun, rejectFun) {
// 返回全新promise
return new MyPromise((resolve, reject) => {
// 定义新方法
const _resolveFun = (val) => {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
const result = resolveFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
};
const _rejectFun = (val) => {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
const result = rejectFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
};
// 将回调添加进各自队列
this.resolveQueue.push(_resolveFun);
this.rejectQueue.push(_rejectFun);
});
}
这里的重点是我们返回了一个新的MyPromise
对象,并且新写了resolve
和reject
方法,然后加入各自的队列。
对于新的resolve
和reject
方法,我们先执行老方法获取结果,然后针对结果的类型进行不同的处理。对于回调返回普通值,我们只需要resolve
就可以了(因为执行resolve
实际上就相当于在执行then
里面的resolve
回调函数)。如果是promise
我们就调用.then
将resolve
和reject
传递过去(传递过去后,在外部调用resolve
和reject
实际上就相当于在调用里面的resolve
和reject
)。
这里是比较难理解的。需要反复多看几次。😇
好啦我们来测试下链式调用
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
mp1
.then((res) => {
console.log(res);
return "success2";
})
.then((res) => {
console.log(res);
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success3");
});
});
})
.then((res) => {
console.log(res);
});
依次输出了success success2 success3
,链式调用也实现了。
到这里我们的promise
就完美了吗?答案是否定的。它还存在很多问题。
问题优化
比如 值穿透。
值穿透就是我们可以一直.then()
,结果会一直传递。
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
mp1.then().then().then((res) => {
console.log(res);
});
直接报错,很明显我们这里是不支持值穿透的。
值穿透
要改这个问题也很简单,就是then
方法中,在将回调添加到回调数组之前进行判断。如果没有传递方法我们需要手动给他添加一个方法。
then(resolveFun, rejectFun) {
// 根据规范,如果then的参数不是function,我们需要手动创建相应函数,让链式调用继续往下执行
typeof resolveFun !== "function"
? (resolveFun = (value) => value)
: null;
typeof rejectFun !== "function"
? (rejectFun = (reason) => {
throw new Error(
reason instanceof Error ? reason.message : reason
);
})
: null;
return new MyPromise((resolve, reject) => {
// ...
});
}
对于resolve
,如果没有传递回调,我们就创建一个返回自身值的函数((value) => value
)当做回调。否则不做处理。
对于reject
,如果没有传递回调,我们就创建一个返回错误的函数((reason) => throw new Error()
)当做回调。否则不做处理。
我们再来测试一下
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
mp1.then().then().then((res) => {
console.log(res);
});
输出success
值穿透问题完美解决。
同步任务
细心的同学可能发现,我们每次在new MyPromise
里面在执行resolve
或reject
的时候都需要写在setTimeout
里面。
const mp1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
那不写到setTimeout
里面可不可以呢?目前肯定是不可以的。
const mp1 = new MyPromise((resolve, reject) => {
resolve("success");
});
mp1.then((res) => {
console.log(res);
});
我们来测一下,返现什么也没输出
原因就是我们在执行resolve/reject
的时候回调数组里面还没回调。所以后面再通过then
方法添加回调也就没有意义了。
那怎么解决这个问题呢?
其实只要我们的resolve/reject
方法异步执行就可以了。这样就会先执行then
收集所有回调函数,后面resolve/reject
的时候再触发回调。
我们知道,异步有宏任务和微任务之分,由于原生Promise
的then
是微任务,所以我们这里也使用微任务。我们这里使用MutationObserver
来模拟微任务。
// ...
_runMicroTask(callback) {
// 使用浏览器MutationObserver WEB.API实现then方法的微任务机制
let count = 0;
const observer = new MutationObserver(callback);
// 创建文本节点,节约资源
const textNode = document.createTextNode(String(count));
observer.observe(textNode, {
// 当文本改变时触发回调
characterData: true,
});
// 改变文本,回调callback触发
textNode.data = String(++count);
}
constructor(executor) {
// 实现resolve
const resolve = (val) => {
const run = () => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = FULFILLED;
while (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(val);
}
};
// 变成微任务执行
this._runMicroTask(run);
};
// 实现reject
const reject = (val) => {
const run = () => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = REJECTED;
while (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(val);
}
};
// 变成微任务执行
this._runMicroTask(run);
};
// new Promise()时立即执行executor,并传入resolve和reject
executor(resolve, reject);
}
// ...
我们再来测试一下
const mp1 = new MyPromise((resolve, reject) => {
resolve("success");
});
mp1.then((res) => {
console.log(res);
});
诶,输出success
。这样对于同步的resolve/reject
也能正确输出啦。
状态已变更
下面我们再来看个例子。
const mp1 = new MyPromise((resolve, reject) => {
resolve("success");
});
mp1.then((res) => {
console.log(res);
});
setTimeout(() => {
mp1.then((res) => {
console.log(res);
});
});
上面会输出几个success
呢?诶,这个不是上面说过了的吗。多次调用多次执行,会输出两个success
。答案真的是两次吗?
我们运行测试一下
啊,只输出了一次。这是怎么回事呢?其实就是在resolve("success")
执行之前只收集到了第一个then
的回调,执行完后变成fulfilled
的了,后面再从setTimeout
里面收集到回调就不会再执行了。
那要怎么解决这个问题呢?那还得从我们的then
方法做文章了。在then
方法里面我们得判断,如果当前状态不是pending
,我们的回调应该要立即执行。并且还要将上一次执行的结果传递过去。
// ...
// 定义变量存储最近一次promise返回的值
value = null;
// ...
constructor(executor) {
// 实现resolve
const resolve = (val) => {
const run = () => {
// ...
// 变更状态
this.status = FULFILLED;
// 存储最近一次执行的值
this.value = val;
while (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(val);
}
};
this._runMicroTask(run);
};
// 实现reject
const reject = (val) => {
const run = () => {
// ...
// 变更状态
this.status = REJECTED;
// 存储最近一次执行的值
this.value = val;
while (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(val);
}
};
this._runMicroTask(run);
};
// new Promise()时立即执行executor,并传入resolve和reject
executor(resolve, reject);
}
then(resolveFun, rejectFun) {
// ...
return new MyPromise((resolve, reject) => {
// ...
// 状态判断,不是PENDING 立即执行回调
switch (this.status) {
case PENDING:
// 将回调添加进各自队列
this.resolveQueue.push(_resolveFun);
this.rejectQueue.push(_rejectFun);
break;
case FULFILLED:
_resolveFun(this.value);
break;
case REJECTED:
_rejectFun(this.value);
break;
}
});
}
// ...
好,我们再来测试
const mp1 = new MyPromise((resolve, reject) => {
resolve("success");
});
mp1.then((res) => {
console.log(res);
});
setTimeout(() => {
mp1.then((res) => {
console.log(res);
});
});
输出两个success
,对于状态变更后续添加的回调我们也能解决。
错误处理
下面我们再来看一个例子
const mp1 = new MyPromise((resolve, reject) => {
throw new Error("出错啦");
});
mp1.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
我们发现,抛出的错误并没有按理想进入我们的then
方法的第二个回调。而是直接报错。
对于 new MyPromise()
里面出现的不可预测的错误的处理,其实只要在我们构造函数执行executor
的时候处理就好了。
constructor(executor) {
// ...
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
我们再来测试一下,发现错误已经被我们处理啦。不再是 uncaught Error
啦。
对于then
里面抛出的错误我们还没处理
const mp1 = new MyPromise((resolve, reject) => {
resolve("success");
});
mp1
.then(
(res) => {
console.log(res);
throw new Error("出错啦");
},
(err) => {
console.log(err);
}
)
.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
我们再来处理下then
里面的错误。then
里面抛出的错误,我们需要在执行原始回调的时候处理就可以了。
then(resolveFun, rejectFun) {
// ...
return new MyPromise((resolve, reject) => {
// 定义新方法
const _resolveFun = (val) => {
// 添加 try catch 处理错误
try {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
const result = resolveFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
} catch (error) {
reject(error);
}
};
const _rejectFun = (val) => {
// 添加 try catch 处理错误
try {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
const result = rejectFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
} catch (error) {
reject(error);
}
};
// ...
});
}
我们再来测试一下,发现错误已经被我们处理啦。不再是 uncaught Error
啦。
初步完整代码
好啦,现在我们的promise
基本上就已经符合Promise A+规范
啦。
// 定义Promise/A+规范的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
// 定义两个数组,存储回调
resolveQueue = [];
rejectQueue = [];
status = PENDING;
value = null;
// 模拟微任务执行回调
_runMicroTask(callback) {
// 使用浏览器MutationObserver WEB.API实现then方法的微任务机制
let count = 0;
const observer = new MutationObserver(callback);
// 创建文本节点,节约资源
const textNode = document.createTextNode(String(count));
observer.observe(textNode, {
// 当文本改变时触发回调
characterData: true,
});
// 改变文本,回调callback触发
textNode.data = String(++count);
}
constructor(executor) {
// 实现resolve
const resolve = (val) => {
const run = () => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = FULFILLED;
// 存储最近一次执行的值
this.value = val;
while (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(val);
}
};
this._runMicroTask(run);
};
// 实现reject
const reject = (val) => {
const run = () => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = REJECTED;
// 存储最近一次执行的值
this.value = val;
while (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(val);
}
};
this._runMicroTask(run);
};
// new Promise()时立即执行executor,并传入resolve和reject
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(resolveFun, rejectFun) {
// 根据规范,如果then的参数不是function,我们需要手动创建相应函数,让链式调用继续往下执行
typeof resolveFun !== "function"
? (resolveFun = (value) => value)
: null;
typeof rejectFun !== "function"
? (rejectFun = (reason) => {
throw new Error(
reason instanceof Error ? reason.message : reason
);
})
: null;
return new MyPromise((resolve, reject) => {
// 定义新方法
const _resolveFun = (val) => {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
try {
const result = resolveFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
} catch (error) {
reject(error);
}
};
const _rejectFun = (val) => {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
try {
const result = rejectFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
} catch (error) {
reject(error);
}
};
// 判断状态,解决状态已变更问题
switch (this.status) {
case PENDING:
// 将回调添加进各自队列
this.resolveQueue.push(_resolveFun);
this.rejectQueue.push(_rejectFun);
break;
case FULFILLED:
_resolveFun(this.value);
break;
case REJECTED:
_rejectFun(this.value);
break;
}
});
}
}
我们知道,promise
其实还是提供了一些其它方法,下面我们也来实现一下。
实现其它方法
Promise.resolve()
Promise.resolve(value)
返回一个 promise 并且是 fulfilled 状态的对象
static resolve(value) {
// 根据规范, 如果参数是Promise实例, 直接return这个实例
// 否则返回一个新的promise并且是fulfilled状态
if(value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
Promise.reject()
Promise.reject()
方法返回一个 promise 并且是 reject 状态的对象
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
Promise.prototype.catch()
catch()方法
返回一个Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。
// catch方法其实就是执行一下then的第二个回调
catch(rejectFn) {
return this.then(undefined, rejectFn)
}
我们来测试一下
const mp1 = new MyPromise((resolve, reject) => {
resolve("success");
});
mp1
.then(
(res) => {
console.log(res);
throw new Error("出错啦");
}
)
.catch((err) => {
console.log("catch err", err);
});
错误正常捕获
Promise.prototype.finally()
finally()方法
接受一个回调函数,但是该回调函数没有参数。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。并且返回一个Promise,也就是在finally之后,还可以继续then。并且会将值原封不动的传递给后面的then。
finally(callback) {
return this.then(
(res) => {
callback();
return MyPromise.resolve(res);
},
(err) => {
callback();
return MyPromise.reject(err);
}
);
}
我们来测试一下
const mp1 = new MyPromise((resolve, reject) => {
resolve("success");
});
mp1
.then(
(res) => {
console.log(res);
throw new Error("出错啦");
},
(err) => {
console.log(err);
}
)
.catch((err) => {
console.log("catch err", err);
return 123;
})
.finally(() => {
console.log("不管成功失败我都会执行");
})
.then((res) => {
console.log(res);
});
finally
总会执行,并且返回的是promise
,并且会将值原封不动的传递给后面的then
。
Promise.all()
Promise.all(iterable)
参数为一组Promise
实例组成的数组,用于将多个Promise
实例包装成一个新的Promise
实例。当数组中的
Promise
实例都为都Resolved
的时候,Promise.all()
的状态才会Resolved
,否则为Rejected
。并且Rejected
是第一个被Rejected
的Promise
的返回值。
static all(promiseArr) {
return new MyPromise((resolve, reject) => {
const length = promiseArr.length;
let count = 0;
let values = [];
for (let i = 0; i < length; i++) {
const p = promiseArr[i];
// MyPromise.resolve()处理传入值不为Promise的情况
MyPromise.resolve(p).then(
(res) => {
values[i] = res;
count++;
if (count === length) {
resolve(values);
}
},
(err) => {
// 只要子元素promise中有任何一个reject,则返回的promise rejected
reject(err);
}
);
}
});
}
我们来测试一下
const mp2 = MyPromise.resolve("all success");
const mp3 = MyPromise.resolve("all success2");
MyPromise.all([mp2, mp3]).then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
执行正确,返回所有成功结果
Promise.race()
race
方法与all
方法不同,race
方法中只要对象中有一个状态改变了,它的状态就跟着改变,并将那个改变状态实例的返回值传递给回调函数。也就是不管失败与成功第一个
Promise
的返回值就是race
方法的返回值。resolve
就会进入then
方法,否则进入catch
方法
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
for (let p of promiseArr) {
// MyPromise.resolve()处理传入值不为Promise的情况
MyPromise.resolve(p).then(
(res) => {
resolve(res);
},
(err) => {
reject(err);
}
);
}
});
}
来测试一下
const mp4 = MyPromise.resolve("race success");
const mp5 = MyPromise.reject("race error");
MyPromise.race([mp4, mp5]).then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
执行正确,返回第一个promise
的结果
完整代码
// 定义Promise/A+规范的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
// 定义两个数组,存储回调
resolveQueue = [];
rejectQueue = [];
// 状态
status = PENDING;
// 值
value = null;
// 模拟微任务执行回调
_runMicroTask(callback) {
// 使用浏览器MutationObserver WEB.API实现then方法的微任务机制
let count = 0;
const observer = new MutationObserver(callback);
// 创建文本节点,节约资源
const textNode = document.createTextNode(String(count));
observer.observe(textNode, {
// 当文本改变时触发回调
characterData: true,
});
// 改变文本,回调callback触发
textNode.data = String(++count);
}
constructor(executor) {
// 实现resolve
const resolve = (val) => {
const run = () => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = FULFILLED;
// 存储最近一次执行的值
this.value = val;
while (this.resolveQueue.length) {
const callback = this.resolveQueue.shift();
callback(val);
}
};
this._runMicroTask(run);
};
// 实现reject
const reject = (val) => {
const run = () => {
// 对应规范中的"状态只能由pending到fulfilled或rejected"
if (this.status !== PENDING) {
return;
}
// 变更状态
this.status = REJECTED;
// 存储最近一次执行的值
this.value = val;
while (this.rejectQueue.length) {
const callback = this.rejectQueue.shift();
callback(val);
}
};
this._runMicroTask(run);
};
// new Promise()时立即执行executor,并传入resolve和reject
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(resolveFun, rejectFun) {
// 根据规范,如果then的参数不是function,我们需要手动创建相应函数,让链式调用继续往下执行
typeof resolveFun !== "function"
? (resolveFun = (value) => value)
: null;
typeof rejectFun !== "function"
? (rejectFun = (reason) => {
throw new Error(
reason instanceof Error ? reason.message : reason
);
})
: null;
return new MyPromise((resolve, reject) => {
// 定义新方法
const _resolveFun = (val) => {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
try {
const result = resolveFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
} catch (error) {
reject(error);
}
};
const _rejectFun = (val) => {
// 执行老方法获取结果,以便区分返回的是普通值还是promise
try {
const result = rejectFun(val);
result instanceof MyPromise
? result.then(resolve, reject)
: resolve(result);
} catch (error) {
reject(error);
}
};
switch (this.status) {
case PENDING:
// 将回调添加进各自队列
this.resolveQueue.push(_resolveFun);
this.rejectQueue.push(_rejectFun);
break;
case FULFILLED:
_resolveFun(this.value);
break;
case REJECTED:
_rejectFun(this.value);
break;
}
});
}
//静态的resolve方法
static resolve(value) {
if (value instanceof MyPromise) return value; // 根据规范, 如果参数是Promise实例, 直接return这个实例
return new MyPromise((resolve) => resolve(value));
}
//静态的reject方法
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason));
}
catch(rejectFn) {
return this.then(undefined, rejectFn);
}
finally(callback) {
return this.then(
(res) => {
callback();
return MyPromise.resolve(res);
},
(err) => {
callback();
return MyPromise.reject(err);
}
);
}
static all(promiseArr) {
return new MyPromise((resolve, reject) => {
const length = promiseArr.length;
let count = 0;
let values = [];
for (let i = 0; i < length; i++) {
const p = promiseArr[i];
// MyPromise.resolve()处理传入值不为Promise的情况
MyPromise.resolve(p).then(
(res) => {
values[i] = res;
count++;
if (count === length) {
resolve(values);
}
},
(err) => {
// 只要子元素promise中有任何一个reject,则返回的promise rejected
reject(err);
}
);
}
});
}
// 添加静态race方法
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
for (let p of promiseArr) {
// MyPromise.resolve()处理传入值不为Promise的情况
MyPromise.resolve(p).then(
(res) => {
resolve(res);
},
(err) => {
reject(err);
}
);
}
});
}
}
好啦,关于手写promise
就讲到这里啦。小伙伴们是否学会了呢?🤭
扩展
既然分析到了promise
,我们干脆再来分析下async、await
async await
我们知道,async
和 await
是既能达到暂停执行又能达到异步执行。那它到底是怎么实现这种效果的呢?
其实是promise
和 generator
的语法糖。对generator
不熟悉的可以先看看笔者写的js异步编程。因为promise
它是异步执行的,generator
又能实现暂停执行。它们组合刚好能达到async
和 await
这样的效果。
下面我们来看个async
和 await
的例子
const p1 = Promise.resolve("success1");
const p2 = Promise.resolve("success2");
const p3 = Promise.resolve("success3");
const test = async () => {
const result3 = await p3;
console.log(result3);
const result1 = await p1;
console.log(result1);
const result2 = await p2;
console.log(result2);
};
console.log("同步代码执行啦");
test(); // 同步代码执行啦 success3 success1 success2
async
和 await
中的代码是异步并且是按顺序执行的。
下面我们使用promise
和 generator
来改造一下
const p1 = Promise.resolve("success1");
const p2 = Promise.resolve("success2");
const p3 = Promise.resolve("success3");
function* myGenerator() {
yield p3;
yield p1;
yield p2;
}
// 手动执行迭代器
const gen = myGenerator();
gen.next().value.then((res) => {
console.log(res);
gen.next().value.then((res) => {
console.log(res);
gen.next().value.then((res) => {
console.log(res);
});
});
});
诶,确实也能达到同样的效果,但是需要自己手动调用next
一步一步执行,并且每次异步执行的结果在generator
函数也获取不到。
我们知道generator
是能传递参数的,所以我们再来改造一下
const p1 = Promise.resolve("success1");
const p2 = Promise.resolve("success2");
const p3 = Promise.resolve("success3");
function* myGenerator() {
const result3 = yield p3;
console.log(result3);
const result1 = yield p1;
console.log(result1);
const result2 = yield p2;
console.log(result2);
}
// 手动执行迭代器
const gen = myGenerator();
gen.next().value.then((res3) => {
gen.next(res3).value.then((res1) => {
gen.next(res1).value.then((res2) => {
gen.next(res2);
});
});
});
诶,也能正常输出,并且能在generator
函数获取到结果啦。但是呢还需要手动执行next
。能不能自动执行呢?
下面我们来封装一个自动执行函数,让它自动执行
const p1 = Promise.resolve("success1");
const p2 = Promise.resolve("success2");
const p3 = Promise.resolve("success3");
const _run = (gen) => {
// 获取迭代器
const it = gen();
// 封装递归方法
const _next = (val) => {
const result = it.next(val);
if (result.done) return result.value;
result.value.then((res) => {
_next(res);
});
};
// 第一次调用
_next();
};
_run(myGenerator);
测试一下,也能正常输出。
但是现在的async和await
还是有些问题的。
- 需要兼容基本类型:这段代码能自动执行的前提是
yield
后面跟Promise
,为了兼容后面跟着基本类型值的情况,我们需要把yield
跟的内容(gen().next.value
)都用Promise.resolve()
转化一遍 - 缺少错误处理:上边代码里的
Promise
如果执行失败,或者里面同步代码抛错,就会导致后续执行直接中断,我们需要通过调用Generator.prototype.throw()
和reject(error)
,把错误抛出来,才能被外层的try-catch
捕获到 - 返回值是Promise:
async/await
的返回值是一个Promise
,我们这里也需要保持一致,方法返回一个Promise
就可以啦。
问题优化
我们新写一个自动执行函数,来将这些问题解决。
const _run2 = (gen) => {
return new Promise((resolve, reject) => {
// 获取迭代器
const it = gen(); // 封装递归方法
const _next = (val) => {
let result = null;
try {
result = it.next(val);
} catch (error) {
reject(error);
}
if (result.done) return resolve(result.value);
Promise.resolve(result.value).then(
(res) => {
_next(res);
},
(err) => {
it.throw(err);
}
);
};
// 第一次调用
_next();
});
};
我们来测试一下
const p4 = Promise.resolve("success4");
const p5 = Promise.resolve("success5");
const p6 = Promise.reject("error6");
function* myGenerator2() {
try {
const result4 = yield p4;
console.log(result4);
const result5 = yield p5;
console.log(result5);
const result6 = yield p6;
console.log(result6);
} catch (error) {
console.log("异常被捕获啦", error);
}
}
_run2(myGenerator2);
异常被成功捕获了。
到这对于async
和 await
原理也讲得差不多了。核心就是使用promise
和generator
,并实现generator
自动执行的方法。
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!