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