Promise 详解

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 总结

  1. Promise的then要领回调是异步,组织函数内是同步。【内同外异】
  2. Promise状况一旦改变,没法在发作变动。 pending -> fulfilled、 pending -> rejected
  3. Promise的then要领的参数希冀是函数,传入非函数则会发作值穿透。
  4. promise 的.then或许.catch能够被挪用屡次,但这里 Promise 组织函数只实行一次。或许说 promise 内部状况一经改变,而且有了一个值,那末后续每次挪用.then 或许.catch都邑直接拿到该值。
  5. promise 的.then或许.catch返回promise对象【catch要领是then(null, function(error){})的语法糖/省略写法】
  6. 假如挪用 then 时,promise已胜利,则实行 onFulfilled,并将promise的值作为参数通报进去。假如promise已失利,那末实行 onRejected, 并将 promise 失利的缘由作为参数通报进去。假如promise的状况是pending,须要将onFulfilled和onRejected函数寄存起来,守候状况肯定后,再顺次将对应的函数实行(宣布定阅)
  7. then 的参数 onFulfilled 和 onRejected 能够缺省
  8. promise 能够then屡次,promise 的then 要领返回一个 promise
  9. 假如 then 返回的是一个效果,那末就会把这个效果作为参数,通报给下一个then的胜利的回调(onFulfilled)
  10. 假如 then 中抛出了非常,那末就会把这个非常作为参数,通报给下一个then的失利的回调(onRejected)
  11. 假如 then 返回的是一个promise,那末须要等这个promise,那末会等这个promise实行完,promise假如胜利,就走下一个then的胜利,假如失利,就走下一个then的失利
    原文作者:adaxxl
    原文地址: https://segmentfault.com/a/1190000019033980
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞