手写Promise原理

简介:

我的promise能实现什么?
1:解决回调地狱,实现异步
2:可以链式调用,可以嵌套调用
3:有等待态到成功态的方法,有等待态到失败态的方法
4:可以衍生出周边的方法,如Promise.resolve(),Promise.reject(),Promise.prototype.then(),Promise.prototype.catch(),Promise.all() // 所有的完成

  1. 可以根据自己的需求调节自己的promise

let PromiseA = require('./PromiseA');

const promiseA = new PromiseA((resolve, reject) => {

resolve(new PromiseA((resolve,reject)=>{
    setTimeout(()=>{
        resolve(100)
    },1000)
}))

})

promiseA.then(data=>{

console.log(data)

})

下面开始实现promise,首先创造三个常量,等待,成功,失败

const PENDING = 'PENDING'; // 等待状态
const RESOLVED = 'RESOLVED'; // 成功状态
const REJECTED = 'REJECTED'; // 失败状态
然后创造一个promiseA类,里面有constructor,then方法,catch方法。这里的catch其实就是失败的then,即then(null,(err)=>{...})

class PromiseA {
constructor(){...}
then(){...}
    catch(err){
        return this.then(null,err)
    }
}

我们重点关注constructor和then,先来看constructor。这里promiseA默认的状态是等待态,成功的值value默认为undefined,失败的值reason默认为undefined。这里的onResolvedCallbacks和onRejectedCallbacks是一个发布订阅的数组,我们先不管。然后有resolve方法,reject方法

还有Promise自带一个executor执行器,就是传进来的参数。会立即执行 。 但有可能出错。所以用try,catch包住。 executor里有俩个参数,就是resolve和reject。就是promise传进来参数的俩个resolve,reject方法.

constructor(executor) {
    this.status = PENDING; // 默认等待状态
    this.value = undefined;    
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (value) => {                            
        if(value instanceof PromiseA){             
            value.then(resolve,reject)
            return
        }
        if (this.status === PENDING) {
            this.value = value;
            this.status = RESOLVED;
            this.onResolvedCallbacks.forEach(fn => fn());
        }
    }
    let reject = (reason) => {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
            this.onRejectedCallbacks.forEach(fn => fn());
        }
    }
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e)
    }
}

然后我们在看看then,then里面传进来俩个参数,其实就是俩个方法。一个成功的回调,一个失败的回调。我们重点看一下,三个状态的执行,即status的走向。如果是resolve,即执行成功的回调onFulfilled。如果是reject,即执行失败的回调onRejected。如果是等待,即执行一个发布订阅的模式,发布订阅,其实就是,我先将成功的回调或者的失败的回调各自放入对应的数组,即是上面我们跳过的俩个数组onResolvedCallbacks和onRejectedCallbacks 。然后,当状态改变为resolve或者reject的时候,即遍历执行对应的回调函数。至此异步就实现了,回调地狱解决。这个异步解决就是靠发布订阅模式来解决的。

then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v
    onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}
    let promise2 = new PromiseA((resolve, reject) => {
        if (this.status === RESOLVED) {
            setTimeout(() => {
                try {
                    let x = onFulfilled(this.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e)
                }
            });
        }
        if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    let x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e)
                }
            });
        }
        if (this.status === PENDING) {
            this.onResolvedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(err);
                    }
                });

            })
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(err);
                    }
                });
            })
        }
    })
    return promise2;
}

接下来我们继续实现链式调用,既是一个promiseA.then的结果返回的e是promise2的then的data。 一个成功的结果返回给下一个then作为参数。

let promise2 = promiseA.then(e=>{

return e

}
)
promise2.then(data=>{

console.log(data,'123')

})

那么继续实现,还是上面的函数。这里我们重点观察这个promise2和setTimeout和resolvePromise. 我们先说promise2,这里的promise2,其实是一个新的promise.也就是说promise的链式调用靠的就是返回一个新的promise.这里把之前的三种状态包起来,目的就是可以让里面得到的结果,获取给promise2的resolve和reject。有人可能会说,那么promise2的resolve或者reject要还是promise怎么办?这里我们就要用到resolvePromise方法来判断了。所以我们将成功的回调函数resolve换成resolvePromise方法来执行。这里我们要明白,resolve是执行成功的回调函数。不管状态是成功还是失败,如果执行成功都是走向resolve。所以resolve和reject的状态如果执行成功都是走向resolve。

    let promise2 = new PromiseA((resolve, reject) => {
        if (this.status === RESOLVED) {
            setTimeout(() => {
                try {
                    let x = onFulfilled(this.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e)
                }
            });
        }
        if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    let x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e)
                }
            });
        }
        if (this.status === PENDING) {
            this.onResolvedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(err);
                    }
                });

            })
            this.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(err);
                    }
                });
            })
        }
    })
    return promise2;

然后我们在看resolvePromise方法,走到这里,说明是执行成功的回调函数了。传进去的参数有promise2,x,promise2的resolve,promise2的reject。首先promise2是一个new promise。 这样传进去参数是会报错的。因为执行到这一步,promise2还没有生成。所以是会报错的。

所以我们加一个setTimeout包住它,这样就可以等promise2生成完在执行。这里的setTimeout涉及到了事件循环,也就是宏任务和微任务的部分。js执行机制是先执行主线程,然后执行微任务队列,然后进行渲染,然后在执行宏任务队列。宏任务执行完,如果宏任务里还包着js任务,就继续循环反复。直到所有任务执行完成。这里的setTimeout是宏任务,

resolvePromise(promise2, x, resolve, reject);
刚刚说到setTimeout,这里贴上一段代码。这里的newPromise是在setTimeout前执行的。

console.log(1);

setTimeout(() => {
console.log("我是定时器,延迟0S执行的");
}, 0);

new Promise((resolve, reject) => {
console.log("new Promise是同步任务里面的宏任务");
resolve("我是then里面的参数,promise里面的then方法是宏任务里面的微任务");
}).then(data => {
console.log(data);
});

console.log(2);

好的参数都传进去了,接下来我们看resolvePromise的具体方法。就是一个判断回调函数x是不是promise,如果是就在循环拆开。直到不是为止。如果是普通值的话就可以直接返回了。至此,所以的promise库就实现完了。至于后面的all和其他周边方法就是语法糖了。主要核心部分掌握了,后面的周边方法就不算什么了。

function resolvePromise(promise2, x, resolve, reject) {

if (promise2 === x) {
    return reject(new TypeError('返回的promise和当前promise不能是同一个对象哦,会嵌入死循环'))
}
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    try {
        let then = x.then;
        if (typeof then === 'function') {
            then.call(x,y=> {
                resolvePromise(promise2, y, resolve, reject)
            },r=> {
                reject(r)
            }
            )
        } else {
            resolve(x)
        }
    } catch (err) {
        reject(err)
    }
} else {
    resolve(x);
}

}

至于resolve方法里判断,我们来看看。其实也是一个递归。判断PromiseA里的resolve里面是不是promise,一直拆开。跟上面的方法类似。

    let resolve = (value) => {
        if(value instanceof PromiseA){
            value.then(resolve,reject)
            return
        }
        if (this.status === PENDING) {
            this.value = value;
            this.status = RESOLVED;
            this.onResolvedCallbacks.forEach(fn => fn());
        }
    }

而至于这俩行代码,就是解决一个不断向jquery那样then的情况。如果是函数的话,将自己的参数作为结果返回。传递给下一个then。

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v
    onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}

promiseA.then().then().then().then().then()
原文地址https://www.cnblogs.com/At867604340/p/12486678.html

相关文章
|
前端开发
Promise的用法&原理&手写实现-2
Promise的用法&原理&手写实现-2
49 1
|
7月前
|
存储 前端开发 API
技术笔记:Promise的原理探究及手写Promise
技术笔记:Promise的原理探究及手写Promise
40 0
|
8月前
|
存储 前端开发 安全
快速了解std::promise的工作原理和使用
快速了解std::promise的工作原理和使用
226 3
|
8月前
|
前端开发 JavaScript API
Promise.all() 的原理与实战:简化异步逻辑的不二选择
Promise.all() 的原理与实战:简化异步逻辑的不二选择
Promise.all() 的原理与实战:简化异步逻辑的不二选择
|
前端开发 JavaScript API
Promise的用法&原理&手写实现-1
Promise的用法&原理&手写实现-1
65 0
|
前端开发
浏览器原理 18 # Promise 到底解决了什么问题呢?
浏览器原理 18 # Promise 到底解决了什么问题呢?
129 0
浏览器原理 18 # Promise 到底解决了什么问题呢?
|
存储 缓存 自然语言处理
吊打面试官:promise原理详解
吊打面试官:promise原理详解
205 0
|
前端开发
重新手写promise,理解核心的异步链式调用原理
重新手写promise,理解核心的异步链式调用原理
184 0
|
前端开发 JavaScript
通过polyfill理解Promise原理
一个挺经典的前端面试题,自己polyfill实现Promise。在网友实现的基础上,自己理解加上注释。 这个问题涉及到了JavaScript作用域规则、事件循环、函数上下文、原型继承等诸多基础知识,理解完感觉很有收获,以注释形式记录下来。
|
前端开发 JavaScript 测试技术
Promise.race() 原理解析及使用指南
Promise 对象是 ECMAScript 6 中新增的对象,主要将 JavaScript 中的异步处理对象和处理规则进行了规范化。前面介绍了《Promise.any() 原理解析及使用指南》、《Promise.all() 原理解析及使用指南》和《Promise.allSettled() 原理解析及使用指南》
410 0
Promise.race() 原理解析及使用指南