Promise-详解

9次阅读

共计 9523 个字符,预计需要花费 24 分钟才能阅读完成。

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 的失败
正文完
 0