异步编程方案----Promise实现小解

Promise作为异步编程方案callback的进阶版,解决了callback回调地域问题的同时,还增加了一些特性:比如all,race方法是的promise在处理异步编程时更加的强大和灵活。

Promise特性

  1. 构造器需要一个参数executor(执行器)函数,executor同时需要resolvereject两个函数作为参数,resolve是将promise转换为fulfilled(成功态)时调用的函数,reject是将promise转换为rejected(失败态)时调用的函数。
  2. Promise具有三种状态:pending(默认态),fulfilledrejected,只能由pending--->fulfilled或者pending--->rejected。fulfilled和rejected之间不能转换
  3. promise的then函数具有两个参数onFullfilledonRejected两个回调函数,这两个回调函数会在promise确定了状态之后进行对应的调用
  4. onFullfilledonRejected两个回调函数会被添加进微任务队列(延时调用,不过优先级高于setTimeout宏任务)
  5. executor里面如果有setTimeout延时调用,则promise会暂时处于pending状态,此时执行then函数时需要onFullfilledCallbacksonRejectedCallbacks两个回调队列用于分别缓存对应的回调函数
  6. then函数会返回一个新的promise作为下一步的链式调用

根据以上特性初步实现一个最初的promise,代码如下:

class Promise {
    constructor(executor) {
        this.status = 'pending' //默认状态
        this.value = undefined; //成功时保存resolve传过来的参数
        this.reason = undefined; //失败时保存reject传过来的原因
        this.onFullfilledCallbacks = [];//成功时回调队列
        this.onRejectedCallbacks = [];//失败时回调队列

        let resolve = (data) => {
            this.status = 'fullfilled';
            this.value = data;
            this.onFullfilledCallbacks.forEach(fn => fn());
        }

        let reject = (err) => {
            this.status = 'rejected';
            this.reason = err;
            this.onRejectedCallbacks.forEach(fn => fn());
        }

        try {
            executor(resolve, reject);
        } catch (err) {//执行器如果报错直接转为失败态,并且将失败原因传出
            reject(err);
        }
    }

    then(onFullfilled, onRejected) {
        let promise2;//返回新的promise

        if(this.status === 'fullfilled') {//如果成功了
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let x = onFullfilled(this.value);
                        resolve(x);
                    } catch (err) {//失败将错误传出
                        reject(err);
                    }
                }, 10);
            });
        }

        if(this.status === 'rejected') {//如果成功了
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolve(x);//即使失败了下一次then依然会走进成功的回调函数
                    } catch (err) {//失败将错误传出
                        reject(err);
                    }
                }, 10);
            });
        }

        if(this.status === 'pending') {//pending状态需要将对应的回调缓存起来
            promise2 = new Promise((resolve, reject) => {
                this.onFullfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFullfilled(this.value);
                            resolve(x);
                        } catch (err) {//失败将错误传出
                            reject(err);
                        }
                    }, 10);
                });

                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolve(x);//即使失败了下一次then依然会走进成功的回调函数
                        } catch (err) {//失败将错误传出
                            reject(err);
                        }
                    }, 10);
                });
            });
        }

        return promise2;
    }
}
复制代码

then链式调用实现

按照promise A+ 规范,返回值x需要进行以下解析处理:

1. x 与 promise 相等

如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise

2. x 为 Promise

如果 x 为 Promise ,则使 promise 接受 x 的状态:

  • 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
  • 如果 x 处于执行态,用相同的值执行 promise
  • 如果 x 处于拒绝态,用相同的据因拒绝 promise

3. x 为对象或函数

  • 把 x.then 赋值给 then
  • 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
  • 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
    • 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
    • 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
    • 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
    • 如果调用 then 方法抛出了异常 e:
      • 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
      • 否则以 e 为据因拒绝 promise
    • 如果 then 不是函数,以 x 为参数执行 promise

4. 如果 x 不为对象或者函数

以 x 为参数执行 promise

resolvePromise代码实现
function resolvePromise(promise2, x, resolve, reject) {
    let called = false;

    if(promise2 === x) {// x 与 promise 相等
        return reject(new TypeError('循环引用了')); //抛出错误
    }

    if(x instanceof Promise) {
        if(x.status === 'pending') {//如果 x 处于等待态
            x.then(function(value) {
                resolvePromise(promise2, value, resolve, reject);// promise 需保持为等待态直至 x 被执行或拒绝
            }, reject);
        } else {//如果 x 处于执行态或者拒绝态
            x.then(resolve, reject);
        }

        return;
    }

    if(x !== null && (typeof x === 'object' || typeof x === 'function')) {//x 为对象或函数
        try {
            let then = x.then;

            if(typeof then === 'function') {//如果 then 是函数
                then.call(x, function(y) {//resolvePromise以值 y 为参数被调用
                    if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
                        return; //则优先采用首次调用并忽略剩下的调用
                    }

                    called = true;
                    resolvePromise(promise2, y, resolve, reject);//运行 [[Resolve]](promise, y)
                }, function(r) {//如果 rejectPromise 
                    if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
                        return; //则优先采用首次调用并忽略剩下的调用
                    }
    
                    called = true;
                    reject(r);//以 r 为原因拒绝 promise
                });
            } else {//如果 then 不是函数
                resolve(x);//以 x 为参数执行 promise
            }
        } catch (err) {//如果取 x.then 的值时抛出错误
            if(called) {//如果 resolvePromise 或 rejectPromise 已经被调用
                return; //忽略之
            }

            called = true;
            reject(err);//以 e 为原因拒绝 promise
        }
    } else {//如果 x 不为对象或者函数
        resolve(x);//以 x 为参数执行 promise
    }
}
复制代码

其他补充

1. Promise的then链式调用还有个特点,就是在中间then没有传入参数时可以依次传递数据,例如:

Promise.resolve('aaaa').then().then().then(function(data) {console.log(data)});

这个特性是因为onFullfilled和onRejected在没有被传入时会被赋予默认值,如下:

if(typeof onFullfilled !== 'function') {
    onFullfilled = value => value;
}

if(typeof onRejected !== 'function') {
    onRejected = err => {
        throw err;
    }
}
复制代码

2. Promise实例的catch方法类似不传onFullfilled参数的then方法,实现如下:

catch(callback) {
    return this.then(null, callback);
}
复制代码

3. Promise的静态all方法能够在传入的所以promise成功后返或数据,其中有一个失败则全部失败。

static all(promises) {
    let dataArr = [];
    let len = promises.length;
    let count = 0;//统计成功次数

    return new Promise(function(resolve, reject) {
        for (let i = 0;i < len;i++) {
            p.then(function(data) {
                dataArr[i] = data;
                
                if(++count === len) {//所有promise成功
                    resolve(dataArr);
                }
            }, reject);
        }
    });
}
复制代码

4. Promise的静态race方法使得传入的所以promise中最先成功的那个结果返回,有一个成功就成功

static race(promises) {
    let len = promises.length;

    return new Promise(function(resolve, reject) {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(resolve, reject);
        }
    });
}
复制代码

完整的Promise实现

class Promise {
    constructor(executor) {
        this.status = 'pending' //默认状态
        this.value = undefined; //成功时保存resolve传过来的参数
        this.reason = undefined; //失败时保存reject传过来的原因
        this.onFullfilledCallbacks = [];//成功时回调队列
        this.onRejectedCallbacks = [];//失败时回调队列

        let resolve = (data) => {
            this.status = 'fullfilled';
            this.value = data;
            this.onFullfilledCallbacks.forEach(fn => fn());
        }

        let reject = (err) => {
            this.status = 'rejected';
            this.reason = err;
            this.onRejectedCallbacks.forEach(fn => fn());
        }

        try {
            executor(resolve, reject);
        } catch (err) {//执行器如果报错直接转为失败态,并且将失败原因传出
            reject(err);
        }
    }

    then(onFullfilled, onRejected) {
        let promise2;//返回新的promise

        if(typeof onFullfilled !== 'function') {
            onFullfilled = value => value;
        }

        if(typeof onRejected !== 'function') {
            onRejected = err => {
                throw err;
            }
        }

        if(this.status === 'fullfilled') {//如果成功了
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let x = onFullfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (err) {//失败将错误传出
                        reject(err);
                    }
                }, 10);
            });
        }

        if(this.status === 'rejected') {//如果成功了
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);//即使失败了下一次then依然会走进成功的回调函数
                    } catch (err) {//失败将错误传出
                        reject(err);
                    }
                }, 10);
            });
        }

        if(this.status === 'pending') {//pending状态需要将对应的回调缓存起来
            promise2 = new Promise((resolve, reject) => {
                this.onFullfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFullfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (err) {//失败将错误传出
                            reject(err);
                        }
                    }, 10);
                });

                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);//即使失败了下一次then依然会走进成功的回调函数
                        } catch (err) {//失败将错误传出
                            reject(err);
                        } 
                    }, 10);
                });
            });
        }

        return promise2;
    }

    catch(callback) {
        return this.then(null, callback);
    }

    static all(promises) {
        let dataArr = [];
        let len = promises.length;
        let count = 0;//统计成功次数

        return new Promise(function(resolve, reject) {
            for (let i = 0;i < len;i++) {
                p.then(function(data) {
                    dataArr[i] = data;
                    
                    if(++count === len) {//所有promise成功
                        resolve(dataArr);
                    }
                }, reject);
            }
        });
    }

    static race(promises) {
        let len = promises.length;

        return new Promise(function(resolve, reject) {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(resolve, reject);
            }
        });
    }

    static deferred() {
        let dfd = {};

        dfd.promise = new Promise(function (resolve, reject) {
            dfd.resolve = resolve;
            dfd.reject = reject;
        });

        return dfd;
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    let called = false;

    if(promise2 === x) {// x 与 promise 相等
        return reject(new TypeError('循环引用了')); //抛出错误
    }

    if(x instanceof Promise) {
        if(x.status === 'pending') {//如果 x 处于等待态
            x.then(function(value) {
                resolvePromise(promise2, value, resolve, reject);// promise 需保持为等待态直至 x 被执行或拒绝
            }, reject);
        } else {//如果 x 处于执行态或者拒绝态
            x.then(resolve, reject);
        }

        return;
    }

    if(x !== null && (typeof x === 'object' || typeof x === 'function')) {//x 为对象或函数
        try {
            let then = x.then;

            if(typeof then === 'function') {//如果 then 是函数
                then.call(x, function(y) {//resolvePromise以值 y 为参数被调用
                    if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
                        return; //则优先采用首次调用并忽略剩下的调用
                    }

                    called = true;
                    resolvePromise(promise2, y, resolve, reject);//运行 [[Resolve]](promise, y)
                }, function(r) {//如果 rejectPromise 
                    if(called) {//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次
                        return; //则优先采用首次调用并忽略剩下的调用
                    }
    
                    called = true;
                    reject(r);//以 r 为原因拒绝 promise
                });
            } else {//如果 then 不是函数
                resolve(x);//以 x 为参数执行 promise
            }
        } catch (err) {//如果取 x.then 的值时抛出错误
            if(called) {//如果 resolvePromise 或 rejectPromise 已经被调用
                return; //忽略之
            }

            called = true;
            reject(err);//以 e 为原因拒绝 promise
        }
    } else {//如果 x 不为对象或者函数
        resolve(x);//以 x 为参数执行 promise
    }
}
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值