重新认识Promise(一)

152 阅读5分钟


一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情

🎉前言

Promise 不管是日常开发还是面试中,都是大家经常碰到的,所以掌握它是很有必要的,了解它的原理就能在遇到问题时能够更快的定位哦。

🎅 ​基础实现

原生的 Promise 代码:

let a = new Promise((resolve,reject) => {
    resolve('成功')
    reject('失败')
})
console.log('a', a);
​
let b = new Promise((resolve,reject) => {
    reject('失败')
})
console.log('b', b);
​
let c = new Promise((resolve,reject) => {
    throw('失败')
})
console.log('c', c);

打印结果为:

image-20220426210922763.png

上述结果可以总结出四个知识:

  1. 执行 resolve 时,PromiseState 会变成 fulfilled
  2. 执行 reject 时,PromiseState 会变成 rejected
  3. a 代码代码块中,即执行 resolve 又执行 rejectPromiseState 以第一次为准,即一旦状态改变便不会在变。
  4. 执行 throw 时,PromiseState 会变成 rejected

那么我们只要实现这四个条件,就能有一个最基础版的 Promise 了。

手动实现 Promise 代码:

Promise 的初始状态为 pending

这里很重要的一步是resolve和reject的绑定this,为什么要绑定this呢?这是为了resolve和reject的this指向永远指向当前的MyPromise实例,防止随着函数执行环境的改变而改变。

class MyPromise {
    constructor (executor) {
        // 初始化值
        this.initValue();
        // 初始化this指向
        this.initBind();
​
        // 立即执行两个函数
        executor(this.resolve, this.reject);
    }
​
    initValue () {
        // promise状态默认为 pending
        this.PromiseState = 'pending';
        // Result用来存放最后的结果
        this.PromiseResult = null;
    }
​
    initBind () {
        // 初始化this指向
        this.resolve = this.resolve.bind(this);
        this.reject = this.reject.bind(this);
    }
​
    resolve (value) {
        // 执行 resolve 时,状态变为 fulfilled
        this.PromiseState = 'fulfilled';
        // 结果为传进来的值
        this.PromiseResult = value;
    }
​
    reject (reson) {
        // 执行 resolve 时,状态变为rejected
        this.PromiseState = 'rejected';
        // 结果为传进来的值
        this.PromiseResult = reson;
    }
}

测试代码:

let test1 = new MyPromise((resolve,reject) => {
        resolve('成功')
    })
let test2 = new MyPromise((resolve,reject) => {
    reject('失败')
})
console.log('test1', test1);
console.log('test2', test2);

image-20220426212337097.png

测试上述总结第三点,PromiseState 以第一次为准,即一旦状态改变便不会在变。

let test3 = new MyPromise((resolve,reject) => {
    resolve('成功')
    reject('失败')
})
console.log('test3', test3);

image-20220426213557919.png

厚礼蟹,按照总结,状态应该以第一次为准,也就是 成功fulfilled 才对。

OK,那我们修改一下代码:

resolve (value) {
+    // 如果 状态被改变(不等于pending)时,就返回,下面就不执行了
+    if (this.PromiseState !== 'pending') return;
    // 执行 resolve 时,状态变为 fulfilled
    this.PromiseState = 'fulfilled';
    // 结果为传进来的值
    this.PromiseResult = value;
}
​
reject (reson) {
+    // 如果 状态被改变(不等于pending)时,就返回,下面就不执行了
+    if (this.PromiseState !== 'pending') return;
    // 执行 resolve 时,状态变为rejected
    this.PromiseState = 'rejected';
    // 结果为传进来的值
    this.PromiseResult = reson;
}

再次测试结果:

let test3 = new MyPromise((resolve,reject) => {
    resolve('成功')
    reject('失败')
})
console.log('test3', test3);

image-20220426214648197.png

nice! 成功了。

测试上述总结第四点,执行 throw 时,PromiseState 会变成 rejected

Promise 中,如果有 throw 的话,就会执行 reject 。我们可以利用 try catch 包裹一下。

// 立即执行两个函数
try {
    executor(this.resolve, this.reject);
} catch (e) {
    // 如果错误就执行 reject
    this.reject(e)
}

测试代码:

let test4 = new MyPromise((resolve,reject) => {
    throw('失败')
})
console.log('test4', test4);

image-20220426215345789.png

🎃 then 实现

then 方法是 Promise 的回调函数,它接收两个参数,一个 成功回调,一个 失败回调

手动实现 then 方法:

then (onFulfilled, onRejected) {
    // 校验两个参数是否是函数,这里使用 typeof
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : reson => { throw reson };
​
    // 如果状态为 fulfilled 就执行 onFulfilled
    if (this.PromiseState === 'fulfilled') {
        onFulfilled(this.PromiseResult);
​
        // 否则如果状态为 rejected 就执行 onRejected
    } else if (this.onRejected === 'rejected') {
        onRejected(this.PromiseResult);
    }
}

测试代码:

let test5 = new MyPromise((resolve,reject) => {
    resolve('成功啦,兄嘚!!')
}).then(res => {
    console.log(res); // 成功啦,兄嘚!!
}, err => {
    console.log(err);
})

OK,又实现了一个功能~~

👻 ​延迟调用

先测试一下如果在代码中加入 定时器(延迟) 效果,会发生什么?

let test5 = new MyPromise((resolve,reject) => {
    setTimeout(() => {
        resolve('成功啦,兄嘚!!')
    });
}).then(res => {
    console.log(res); // ???
}, err => {
    console.log(err);
})

image-20220426221045837.png

what? 半年没打印出东西...

因为上面的代码 then 方法是在 executor 立即执行函数执行时就调用了,这时候定时器里面的异步操作可能还没结束呢?也就是说 PromiseState 状态还是 pending ,既然是 pendng 肯定不能立即调onFulfilled或者onRejected的啦。

那什么时候知道 定时器里的结果是成功还是失败呢?我们应该将 onFulfilledonRejected 两个回调保存起来,等到 定时器 里的结果执行后,在执行 resolve 或者 reject 方法调用对应的函数。

因为后面可能还有链式调用,按道理应该用 数组 保存起来,但是我们这里先用一个变量来保存,等实现链式调用后我们再稍作修改。

// 初始化值
initValue () {
    //...省略代码
​
    // 把回调结果保存起来
+    this.onFulfilled = null;
+    this.onRejected = null;
}
​
​
resolve (value) {
    //...省略代码
​
+    // 执行保存的函数
+    this.onFulfilled(value);
}
​
reject (reson) {
     //...省略代码
​
+    // 执行保存的函数
+    this.onRejected(reson);
}
​
then (onFulfilled, onRejected) {
    //...省略代码
​
    // 如果状态为 fulfilled 就执行 onFulfilled
    if (this.PromiseState === 'fulfilled') {
        onFulfilled(this.PromiseResult);
​
        // 否则如果状态为 rejected 就执行 onRejected
    } else if (this.onRejected === 'rejected') {
        onRejected(this.PromiseResult);
+    } else {
+        // 否则 就是 pending 状态,保存两个回调函数
+        this.onFulfilled = onFulfilled;
+        this.onRejected = onRejected;
+    }
}

测试代码:

let test5 = new MyPromise((resolve,reject) => {
    setTimeout(() => {
        resolve('成功啦,兄嘚!!')
    }, 1000);
}).then(res => {
    console.log('res:', res); // 1秒后,打印 成功啦,兄嘚!!
}, err => {
    console.log(err);
})

成功实现啦~~

✨总结

上面讲述的 Promise 原理功能实现还没完结,应该还有 链式调用allraceallSettledany 等功能,这些我放到第二期在讲述,大家可以先把上述例子多写几遍,加深印象。有能力者,可以把剩下的功能自己实现一下哦~~

以上就是本次分享的全部内容~~

如果觉得文章写得不错,对你有所启发的,请不要吝啬 点个 关注 并在 评论区 留下你宝贵的意见哦~~😃