前言
📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!
🍅 个人主页:南木元元
在之前的文章中我们已经了解了Promise的基本用法,今天我们就来手写实现一下Promise。
目录
resolve和reject方法
Promise其实就是一个容器,里面保存异步操作的结果。来看一段Promise的代码:
let p1 = new Promise((resolve, reject) => {
resolve('成功')
reject('失败')
})
console.log('p1', p1)
let p2 = new Promise((resolve, reject) => {
reject('失败')
resolve('成功')
})
console.log('p2', p2)
let p3 = new Promise((resolve, reject) => {
throw('报错')
})
console.log('p3', p3)
在浏览器输出一下看看:
我们来分析一下其中的原理:
1.Promise 是一个类,在执行这个类的时候会传入一个执行器,这个执行器会立即执行。
2.执行了resolve,Promise状态会变成fulfilled。
3.
执行了reject,Promise状态会变成rejected。
4.Promise只以第一次为准,状态一经改变,就无法再被改变了。
5.Promise中有throw的话,就相当于执行了reject。
下面就来一一实现。
1.实现resolve和reject
新建一个 MyPromise 类,传入执行器 executor。
// 新建 MyPromise 类
class MyPromise {
// 构造方法,executor是一个执行器,即为传进来的函数 (resolve, reject) => {}
constructor(executor){
// 进入会立即执行executor
executor()
}
}
初始化,接收传入的 resolve 和 reject 方法。其中有很重要的一步是绑定this,这是为了让resolve和reject的this指向永远指向当前的MyPromise实例,防止随着函数执行环境的改变而改变。
class MyPromise {
// 构造方法,executor是一个执行器,即为传进来的函数 (resolve, reject) => {}
constructor(executor) {
// 初始化值
this.result = null; // 终值
this.state = "pending"; // 状态
// 绑定this
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
// 执行传进来的函数
executor(this.resolve, this.reject)
}
resolve(value) {
// 如果执行resolve,状态变为fulfilled
this.state = "fulfilled"
// 终值为传进来的值
this.result = value
}
reject(reason) {
// 如果执行reject,状态变为rejected
this.state = "rejected"
// 终值为传进来的reason
this.result = reason
}
}
现在来测试一下代码:
const test1 = new MyPromise((resolve, reject) => {
resolve('成功')
})
console.log(test1)
const test2 = new MyPromise((resolve, reject) => {
reject('失败')
})
console.log(test2)
2.状态不可变
上面的代码是有问题的。
const test1 = new MyPromise((resolve, reject) => {
resolve('成功')
reject('失败')
})
console.log(test1)
正确的应该是fulfilled成功状态,这里明显没有以第一次为准,状态没有凝固。
解决起来很简单,只需加个条件判断:
resolve(value) {
// state是不可变的,只有状态是等待,才执行状态修改
if (this.state === "pending") {
// 如果执行resolve,状态变为fulfilled
this.state = "fulfilled";
// 终值为传进来的值
this.result = value;
}
}
reject(reason) {
// state是不可变的,只有状态是等待,才执行状态修改
if (this.state === "pending") {
// 如果执行reject,状态变为rejected
this.state = "rejected";
// 终值为传进来的reason
this.result = reason;
}
}
再来测试下:
3.throw
Promise中有throw的话,就相当于执行了reject。使用try...catch来捕获错误:
try {
// 执行传进来的函数
executor(this.resolve, this.reject);
} catch (error) {
// 捕捉到错误直接执行reject
this.reject(error);
}
来测试下:
then方法
then 方法接收两个回调,一个是成功回调,一个是失败回调,内部做的事情就是进行状态判断:
- 如果Promise状态是fulfilled则执行成功回调
- 如果Promise状态是rejected则执行失败回调
// 马上输出 ”成功“
const p1 = new Promise((resolve, reject) => {
resolve('成功')
}).then(res => console.log(res), err => console.log(err))
下面就来一步一步地实现then方法。
1.实现then
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) {
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.state === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.result)
} else if (this.state === 'rejected') {
// 如果当前为失败状态,执行第二哥回调
onRejected(this.result)
}
}
来测试下:
const test = new MyPromise((resolve, reject) => {
resolve('成功')
}).then(res => console.log(res), err => console.log(err))
2.定时器情况
如resolve或reject在定时器里,则需要定时器结束后再执行then。
// 1秒后输出 ”失败“
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('失败')
}, 1000)
}).then(res => console.log(res), err => console.log(err))
上面代码中,怎么才能保证,1秒后才执行then里的失败回调呢?答案是使用数组来保存回调。
为什么使用数组呢?因为一个promise实例可能会多次then。如下:
promise.then(value => {
console.log(1)
console.log('resolve', value)
})
promise.then(value => {
console.log(2)
console.log('resolve', value)
})
promise.then(value => {
console.log(3)
console.log('resolve', value)
})
使用数组就可以一个一个保存下来,后续再去一个个执行。
那如何判断定时器是否执行完毕呢?很简单,只要状态是pending,就证明定时器还没跑完。等定时器结束,执行了resolve或者reject,再去判断要去执行哪个数组中的回调函数。
实现代码:
constructor(executor) {
// 初始化值
this.result = null; // 终值
this.state = "pending"; // 状态
// Promise中有异步逻辑的情况,执行then还是pending状态,暂时保存两个回调
this.onFulfilledCallbacks = []; // 保存成功回调
this.onRejectedCallbacks = []; // 保存失败回调
...
}
resolve(value) {
// state是不可变的,只有状态是等待,才执行状态修改
if (this.state === "pending") {
// 如果执行resolve,状态变为fulfilled
this.state = "fulfilled";
// 终值为传进来的值
this.result = value;
// 执行保存的成功回调(如果Promise中有异步逻辑的情况)
while (this.onFulfilledCallbacks.length) {
// 删除并返回数组中第一个回调函数,然后()调用
this.onFulfilledCallbacks.shift()(this.result);
}
}
}
reject(reason) {
// state是不可变的,只有状态是等待,才执行状态修改
if (this.state === "pending") {
// 如果执行reject,状态变为rejected
this.state = "rejected";
// 终值为传进来的reason
this.result = reason;
// 执行保存的失败回调(如果Promise中有异步逻辑的情况)
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.result);
}
}
}
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) {
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.state === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.result)
} else if (this.state === 'rejected') {
// 如果当前为失败状态,执行第二哥回调
onRejected(this.result)
} else if (this.state === "pending") {
// 如果当前状态为待定状态(即执行then方法的时候还是pending状态,就代表Promise中有异步逻辑),暂时保存两个回调
// 等到异步操作结束,执行成功或失败函数的时候再执行回调
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
}
来测试下:
const test2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 1秒后输出 成功
// resolve('成功') // 1秒后输出 失败
}, 1000)
}).then(res => console.log(res), err => console.log(err))
3.链式调用
then支持链式调用,下一次then执行受上一次then返回值的影响。
// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {
resolve(100)
}).then(res => 2 * res, err => console.log(err))
.then(res => console.log(res), err => console.log(err))
// 链式调用 输出300
const p4 = new Promise((resolve, reject) => {
resolve(100)
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err => console.log(err))
.then(res => console.log(res), err => console.log(err))
从上面示例中,我们可以总结出以下几个点:
1.then方法本身会返回一个新的Promise对象。
2.如果返回值是promise对象,返回值为成功,新promise就是成功。
3.如果返回值是promise对象,返回值为失败,新promise就是失败。
4.如果返回值非promise对象,新promise对象就是成功,值为此返回值。
如何实现then完还能再then呢?答案是then执行后返回一个Promise对象,就能继续then。
代码实现:
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) {
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
//then支持链式调用,本身会返回一个新的Promise对象
var thenPromise = new MyPromise((resolve, reject) => {
//cb是成功或失败的回调函数
const resolvePromise = (cb) => {
try {
//执行回调并保存结果
const x = cb(this.result);
if (x === thenPromise) {
// then方法返回的不能是自身,避免循环引用
throw new Error("不能返回自身");
}
if (x instanceof MyPromise) {
// 如果回调函数的返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道。
x.then(resolve, reject); //调用 then 方法,将其状态变为 fulfilled 或者 rejected
} else {
// 非Promise就直接成功
resolve(x);
}
} catch (err) {
reject(err);
}
};
if (this.state === "fulfilled") {
// 如果当前为成功状态,执行第一个回调(把成功回调传入resolvePromise统一处理)
resolvePromise(onFulfilled);
} else if (this.state === "rejected") {
// 如果当前为失败状态,执行第二个回调(把失败回调传入resolvePromise统一处理)
resolvePromise(onRejected);
} else if (this.state === "pending") {
// 如果当前状态为待定状态(即执行then方法的时候还是pending状态,就代表Promise中有异步逻辑),暂时保存两个回调
// 等到异步操作结束,执行成功或失败函数的时候再执行回调
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
});
// 返回这个包装的Promise
return thenPromise;
}
来测试下:
const test3 = new Promise((resolve, reject) => {
resolve(100) // 输出 状态:成功 值: 200
// reject(100) // 输出 状态:成功 值:300
}).then(res => 2 * res, err => 3 * err)
.then(res => console.log('成功', res), err => console.log('失败', err))
const test4 = new Promise((resolve, reject) => {
resolve(100) // 输出 状态:失败 值:200
// reject(100) // 输出 状态:成功 值:300
}).then(res => new Promise((resolve, reject) => reject(2 * res)), err => new Promise((resolve, reject) => resolve(3 * err)))
.then(res => console.log('成功', res), err => console.log('失败', err))
4.微任务
我们知道,then方法是一个微任务(如果不了解宏任务和微任务,可以去看这篇文章),这里我们使用定时器来让resolvePromise函数异步执行。
实现代码:
//cb是成功或失败的回调函数
const resolvePromise = (cb) => {
//因为then是个微任务,这里使用定时器来模拟,让resolvePromise异步执行就行
setTimeout(() => {
try {
//执行回调并保存结果
const x = cb(this.result);
if (x === thenPromise) {
// then方法返回的不能是自身,避免循环引用
throw new Error("不能返回自身");
}
if (x instanceof MyPromise) {
// 如果回调函数的返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道。
x.then(resolve, reject); //调用 then 方法,将其状态变为 fulfilled 或者 rejected
} else {
// 非Promise就直接成功
resolve(x);
}
} catch (err) {
reject(err);
}
});
};
来测试下:
const test4 = new MyPromise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
好了,到这里,Promise就基本实现了。最后附上完整代码:
class MyPromise {
constructor(executor) {
// 初始化值
this.result = null; // 终值
this.state = "pending"; // 状态
// Promise中有异步逻辑的情况,执行then还是pending状态,暂时保存两个回调
this.onFulfilledCallbacks = []; // 保存成功回调
this.onRejectedCallbacks = []; // 保存失败回调
// 绑定this
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
try {
// 执行传进来的函数
executor(this.resolve, this.reject);
} catch (error) {
// 捕捉到错误直接执行reject
this.reject(error);
}
}
resolve(value) {
// state是不可变的,只有状态是等待,才执行状态修改
if (this.state === "pending") {
// 如果执行resolve,状态变为fulfilled
this.state = "fulfilled";
// 终值为传进来的值
this.result = value;
// 执行保存的成功回调
while (this.onFulfilledCallbacks.length) {
// 删除并返回数组中第一个回调函数,然后()调用
this.onFulfilledCallbacks.shift()(this.result);
}
}
}
reject(reason) {
// state是不可变的,只有状态是等待,才执行状态修改
if (this.state === "pending") {
// 如果执行reject,状态变为rejected
this.state = "rejected";
// 终值为传进来的reason
this.result = reason;
// 执行保存的失败回调
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.result);
}
}
}
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) {
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
//then支持链式调用,本身会返回一个新的Promise对象
var thenPromise = new MyPromise((resolve, reject) => {
//cb是成功或失败的回调函数
const resolvePromise = (cb) => {
//因为then是个微任务,这里使用定时器来模拟,让resolvePromise异步执行就行
setTimeout(() => {
try {
//执行回调并保存结果
const x = cb(this.result);
if (x === thenPromise) {
// then方法返回的不能是自身,避免循环引用
throw new Error("不能返回自身");
}
if (x instanceof MyPromise) {
// 如果回调函数的返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道。
x.then(resolve, reject); //调用 then 方法,将其状态变为 fulfilled 或者 rejected
} else {
// 非Promise就直接成功
resolve(x);
}
} catch (err) {
reject(err);
}
});
};
if (this.state === "fulfilled") {
// 如果当前为成功状态,执行第一个回调(把成功回调传入resolvePromise统一处理)
resolvePromise(onFulfilled);
} else if (this.state === "rejected") {
// 如果当前为失败状态,执行第二个回调(把失败回调传入resolvePromise统一处理)
resolvePromise(onRejected);
} else if (this.state === "pending") {
// 如果当前状态为待定状态(即执行then方法的时候还是pending状态,就代表Promise中有异步逻辑),暂时保存两个回调
// 等到异步操作结束,执行成功或失败函数的时候再执行回调
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
});
// 返回这个包装的Promise
return thenPromise;
}
}
结语
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~