资讯专栏INFORMATION COLUMN

Promise 详解

anquan / 1883人阅读

摘要:被观察者管理内部和的状态转变,同时通过构造函数中传递的和方法以主动触发状态转变和通知观察者。第一个回调函数是对象的状态变为时调用,第二个回调函数是对象的状态变为时调用可选实现主要实现第一步,初步构建。

Promise 含义

Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合合理、强大。所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promies是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理

Promise对象有以下两个特点

对象的状态不受外界影响。

一旦状态改变,就不会再变,任何时候都可以得到这个结果

promise有三种状态,pending(进行中)、fulfilled/resolved(已成功)和rejected(已失败)

Promise优点

将异步操作以同步操作的流程表达出来,避免了回调地狱

Promise对象提供统一的接口,更加容易控制异步操作

Promise缺点

无法中途终止,一旦新建立即执行

如果不设置回调函数,Promise内部抛出的错误,不会反应到外部

当处于pending状态时,无法得知目前进展到哪一个阶段

Promise设计原理

Promise的实现过程,其主要使用了设计模式中的观察者模式:

通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。

被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者。

Promise 是通过.then方法来实现多个异步操作的顺序执行
Promise的内部也有一个 defers 队列存放事件,而 .then 方法的作用和发布订阅模式的on方法一样是用来订阅事件的,每次调用 .then 方法就会往defers队列中放入一个事件,当异步操作完成时, resolve方法标示前一个异步过程完成并从defers队列中取出第一个事件执行并返回当前对象保证链式调用,以此类推,就完成了所有异步过程的队列执行

用法
实例化Promise,返回Promise对象
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

promise.then(function(value){
  // success
}, function(err){
  // failure
})

Promise对象是一个构造函数,用来生成Promise实例。通过new返回一个Promise实例
Promise构造函数接受一个函数作为参数,该函数接受resolve和reject两个回调函数作为参数,它们由 JavaScript 引擎提供,不用自己部署,用来改变Promise的状态

resolve函数,将Promise对象的状态从 pending => resolved,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject函数,将Promise对象的状态从 pending => rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去

Promise.prototype.then

then是Promise构造函数原型链上的方法,Promise.prototype.then,then方法分别指定resolved状态和rejected状态的回调函数

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用【可选】

Promise 实现 主要实现

第一步,初步构建。Promise 的参数是函数 executor,把内部定义 resolve 方法作为参数传到 executor 中,调用 executor。当异步操作成功后会调用 resolve 方法,然后就会执行 then 中注册的回调函数;并通过then中的return this实现链式调用

        function MyPromise(executor) {
            var value = null,
                callbacks = [];  //callbacks为数组,因为可能同时有很多个回调

            this.then = function (onFulfilled) {
                callbacks.push(onFulfilled);
                return this // 返回Promise对象,实现链式调用
            };

            // 成功回调
            function resolve(value) {
                setTimeout(function() { // Promises/A+规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序;通过setTimeout机制,将resolve中执行回调的逻辑放置到JS任务队列末尾,以保证在resolve执行时,then方法的回调函数已经注册完成
                    callbacks.forEach(function (callback) {
                        callback(value);
                    });
                }, 0)
            }

            executor(resolve);
        }

第二步,添加状态state

        function MyPromise(executor) {
            var state = "pending",
                value = null,
                callbacks = [];

            this.then = function (onFulfilled) {
                if (state === "pending") {
                    callbacks.push(onFulfilled);
                    return this;
                }
                onFulfilled(value);
                return this;
            };

            function resolve(newValue) {
                value = newValue;
                state = "fulfilled";
                setTimeout(function () {
                    callbacks.forEach(function (callback) {
                        callback(value);
                    });
                }, 0);
            }

            executor(resolve);
        }

完整实现代码

        function MyPromise(executor) {
            this._status = "PENDING"
            this._value = null
            this._reason = null
            this._resolveFns = []
            this._rejectFns = []

            var handler = function(state, value) {
                if (this.state !== "PENDING") return 

                let promise = this
                let fns = []

                setTimeout(function() {
                    promise.state = state 
                    let st = state === "FULFILLED"
                    fns = st ? promise["_resolveFns"] : promise["_resolveFns"]
                    fns.forEach(fn => {
                        value = fn.call(promise, value) || value
                    })

                    promise[st ? "_value" : "_reason"] = value;
                    promise["_resolveFns"] = promise["_resolveFns"] = undefined;
                }, 0)
            }

            var resolve = function(value) {
                handler.call(this, "FULFILLED", value);
            }

            var reject = function(reason){
                handler.call(this, "REJECTED", reason);
            }
            
            executor(resolve,reject)
        }


        MyPromise.prototype.then = function(resolveFn, rejectFn) {
            var promise = this
            //串行
            return new Promise(function(resolveNext, rejectNext) {
                // 在 then 方法,对then方法的回调函数返回结果 ret 进行判断,如果是一个 promise 对象,就会调用其 then 方法,形成一个嵌套,直到其不是promise对象为止
                function resolveHandler(value) {
                    var result = typeof resolveFn === "function" && resolveFn(value) || value 
                    if (result && typeof result.then === "function") {
                        result.then(function(value) {
                            resolveNext(value)
                        }, function(reason) {
                            rejectNext(reason)
                        })
                    } else {
                        resolveNext(result)
                    }
                }

                function rejectHandler(reason) {
                    var reason = typeof rejectFn === "function" && rejectFn(reason) || reason 
                    rejectNext(reason)
                }

                if(promise._status === "PENDING"){
                    promise._resolveFns.push(resolveHandler);
                    promise._rejectFns.push(rejectHandler);
                }else if(promise._status === "FULFILLED"){ // 状态改变后的then操作,立刻执行
                    resolveHandler(promise._value);
                }else if(promise._status === "REJECTED"){
                    rejectHandler(promise._reason);
                }
                
            })
        }

测试

        // 实际上,fn中的resolve、reject函数分别是then方法中对应的函数参数
        var fn5 = function(resolve, reject){
          console.log("oooo1")
          resolve("5");
          console.log("ssss1")
        }
        var fn6 = function(resolve, reject){
          resolve("6");
        }

        new MyPromise(fn5).then(function(data){
          console.log(data);
          return new MyPromise(fn6);
        }).then(function(data){
          console.log(data);
        });
其他常用 Promise API 实现
MyPromise.prototype.catch = function(reject) {
            return this.then(undefined, reject)
        }

        MyPromise.prototype.delay = function(time) {
            return this.then(function(ori){
                return Promise.delay(ms, value || ori);
            })
        }

        MyPromise.delay = function(ms, value) {
            return new MyPromise(function(resolve, reject){
                setTimeout(function(){
                    resolve(value)
                }, ms)
            })
        }

        MyPromise.resolve = function(value) {
            return new MyPromise(function(resolve, reject){
                resolve(value)
            })
        }

        MyPromise.reject = function(value) {
            return new MyPromise(function(resolve, reject){
                reject(value)
            })
        }

        MyPromise.all = function(promises) {
            return new MyPromise(function(resolve, reject) {
                var result = []
                function resolver(index) {
                    return function(val) {
                        result.push(val)
                        while(--index) {
                            resolve(result)
                        }
                    }
                }

                function rejecter(reason) {
                    reject(reason)
                }

                for (var i = 0; i < promises.length; i++) {
                    promises[i].then(resolver(i), rejecter)
                }
            })
        }

        MyPromise.race = function(promises) {
            return new MyPromise(resolve, reject) {
                function resolver(val) {
                    resolve(val)
                }

                function rejecter(reason) {
                    reject(reason)
                }

                for (var i = 0; i < promises.length; i++) {
                    promises[i].then(resolver, rejecter)
                }
            }
        }
遵循Promise A+ 规范的 Promise 实现 Promise A+ then 规范 Promise决议解决过程
    这个是将promise和一个值x作为输入的一个抽象操作。如果这个x是支持then的,他会尝试让promise接受x的状态;否则,他会用x的值来fullfill这个promise。运行这样一个东西,遵循以下的步骤
        2.3.1 如果promise和x指向同一个对象,则reject这个promise使用TypeError。
        2.3.2 如果x是一个promise,接受他的状态
            2.3.2.1 如果x在pending,promise必须等待x的状态改变
            2.3.2.2 如果x被fullfill,那么fullfill这个promise使用同一个value
            2.3.2.3 如果x被reject,那么reject这个promise使用同一个理由

        2.3.3 如果x是一个对象或者是个方法
            2.3.3.1 then = x.then
            2.3.3.2 如果x.then返回了错误,则reject这个promise使用错误。
            2.3.3.3 如果then是一个function,使用x为this,resolvePromise为一参,rejectPromise为二参,
                2.3.3.3.1 如果resolvePromise被一个值y调用,那么运行[[Resolve]](promise, y)
                2.3.3.3.2 如果rejectPromise被reason r,使用r来reject这个promise
                2.3.3.3.3 如果resolvePromise和rejectPromise都被调用了,那么第一个被调用的有优先权,其他的beihulue
                2.3.3.3.4 如果调用then方法得到了exception,如果上面的方法被调用了,则忽略,否则reject这个promise
            2.3.3.4 如果then方法不是function,那么fullfill这个promise使用x
        2.3.4 如果x不是一个对象或者方法,那么fullfill这个promise使用x

    如果promise产生了环形的嵌套,比如[[Resolve]](promise, thenable)最终唤起了[[Resolve]](promise, thenable),那么实现建议且并不强求来发现这种循环,并且reject这个promise使用一个TypeError。
代码实现
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(executor) {
    let self = this;
    self.status = PENDING;
    self.onFulfilled = [];//成功的回调
    self.onRejected = []; //失败的回调
    //PromiseA+ 2.1
    function resolve(value) {
        if (self.status === PENDING) {
            self.status = FULFILLED;
            self.value = value;
            self.onFulfilled.forEach(fn => fn());//PromiseA+ 2.2.6.1
        }
    }

    function reject(reason) {
        if (self.status === PENDING) {
            self.status = REJECTED;
            self.reason = reason;
            self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2
        }
    }

    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason };
    let self = this;
    //PromiseA+ 2.2.7
    let promise2 = new Promise((resolve, reject) => {
        if (self.status === FULFILLED) {
            //PromiseA+ 2.2.2
            //PromiseA+ 2.2.4 --- setTimeout
            setTimeout(() => {
                try {
                    //PromiseA+ 2.2.7.1
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    //PromiseA+ 2.2.7.2
                    reject(e);
                }
            });
        } else if (self.status === REJECTED) {
            //PromiseA+ 2.2.3
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });
        } else if (self.status === PENDING) {
            self.onFulfilled.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
            self.onRejected.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
        }
    });
    return promise2;
}

        
function resolvePromise(promise2, x, resolve, reject) {
    let self = this;
    //PromiseA+ 2.3.1
    if (promise2 === x) {
        reject(new TypeError("Chaining cycle"));
    }
    if (x && typeof x === "object" || typeof x === "function") {
        let used; //PromiseA+2.3.3.3.3 只能调用一次
        try {
            let then = x.then;
            if (typeof then === "function") {
                //PromiseA+2.3.3
                then.call(x, (y) => {
                    //PromiseA+2.3.3.1
                    if (used) return;
                    used = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, (r) => {
                    //PromiseA+2.3.3.2
                    if (used) return;
                    used = true;
                    reject(r);
                });

            }else{
                //PromiseA+2.3.3.4
                if (used) return;
                used = true;
                resolve(x);
            }
        } catch (e) {
            //PromiseA+ 2.3.3.2
            if (used) return;
            used = true;
            reject(e);
        }
    } else {
        //PromiseA+ 2.3.3.4
        resolve(x);
    }
}

module.exports = Promise;

resolvePromise 函数即为根据x的值来决定promise2的状态的函数,也即标准中的Promise Resolution Procedure;x为promise2 = promise1.then(onResolved, onRejected)onResolved/onRejected的返回值,resolvereject实际上是promise2executor的两个实参,因为很难挂在其它的地方,所以一并传进来。

promise 总结

Promise的then方法回调是异步,构造函数内是同步。【内同外异】

Promise状态一旦改变,无法在发生变更。 pending -> fulfilled、 pending -> rejected

Promise的then方法的参数期望是函数,传入非函数则会发生值穿透。

promise 的.then或者.catch可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用.then 或者.catch都会直接拿到该值。

promise 的.then或者.catch返回promise对象【catch方法是then(null, function(error){})的语法糖/省略写法】

如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)

then 的参数 onFulfilled 和 onRejected 可以缺省

promise 可以then多次,promise 的then 方法返回一个 promise

如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)

如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)

如果 then 返回的是一个promise,那么需要等这个promise,那么会等这个promise执行完,promise如果成功,就走下一个then的成功,如果失败,就走下一个then的失败

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103996.html

相关文章

  • async await详解

    摘要:本身就是的语法糖。类似于后面代码会等内部代码全部完成后再执行打印结果操作符用于等待一个对象。它只能在异步函数中使用。参考附在版本位中是可以直接使用的。持续更新中来点颗吧 async await本身就是promise + generator的语法糖。 本文主要讲述以下内容 async awiat 实质 async await 主要特性 async await 实质 下面使用 pro...

    Shimmer 评论0 收藏0
  • async await详解

    摘要:本身就是的语法糖。类似于后面代码会等内部代码全部完成后再执行打印结果操作符用于等待一个对象。它只能在异步函数中使用。参考附在版本位中是可以直接使用的。持续更新中来点颗吧 async await本身就是promise + generator的语法糖。 本文主要讲述以下内容 async awiat 实质 async await 主要特性 async await 实质 下面使用 pro...

    KavenFan 评论0 收藏0
  • async await详解

    摘要:本身就是的语法糖。类似于后面代码会等内部代码全部完成后再执行打印结果操作符用于等待一个对象。它只能在异步函数中使用。参考附在版本位中是可以直接使用的。持续更新中来点颗吧 async await本身就是promise + generator的语法糖。 本文主要讲述以下内容 async awiat 实质 async await 主要特性 async await 实质 下面使用 pro...

    yedf 评论0 收藏0
  • Promise加载图片用法详解

    摘要:现在不会用都不好意思说自己是前端,为什么火起来,一句话解决了回调嵌套和执行顺序问题最重要的我感觉是解决顺序问题。 现在不会用Promise都不好意思说自己是前端,Promise为什么火起来,一句话解决了回调嵌套和执行顺序问题最重要的我感觉是解决顺序问题。 不过开始写之前我们先看看,promise怎么解决问题,怎么用。列举一个顺序加载图片demo: //需求 加载三张图片 img1,im...

    Brenner 评论0 收藏0

发表评论

0条评论

anquan

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<