Promise

优点:解决并发问题(同步多个异步方法的执行结果)。解决链式调用的问题(先获取a,在获取b) 解决多个回调嵌套的问题。缺点:promise无法终止,只能说是抛弃本次promise的结果。

1. A+规范

  • new Promise时,需要传递一个 executor 执行器,执行器立刻执行。
  • executor 接受两个参数,分别是 resolve 和 reject。
  • promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled。
  • promise 的状态一旦确认,就不会再改变。
  • promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected。
  • 如果调用 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的失败。
  • 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)。
  • 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。
  • 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值。
  • 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。

2. promise测试

  1. npm i promises-aplus-tests -g

  2. promise.js增加以下代码

    Promise.deferred = function(){
      let dfd = {};
      dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
      });
      return dfd;
    }
    
  3. promises-aplus-tests promise.js

3. catch

捕获失败的peomise。

4. Promise.finally

无论如何都执行,finally会传递前一个promise的结果但不会接管该结果,如果finally返回的是promise会等待这个promise执行完成。

5. Promise.try

能捕获同步或者异步异常。需要自己实现。

6. Promise.resolve

返回成功的promise。

7. Promise.reject

返回失败的pomise。

8. Promise.all

返回promise。全部完成才算完成,如果有一个失败,就失败。Promise.all是按照顺序执行的。

9. Promise.race

有一个成功就成功,有一个失败就失败。

10. Promise.stop

终止一个promise。

11. PromiseA+实现

  const PENDING = 'PENDING';
  const FULFILLED = 'FULFILLED';
  const REJECTED = 'REJECTED';
  const resolvePromise = (promise2, x, resolve, reject) => {
    if (promise2 === x) {
      return reject(new TypeError());
    }
    if ((x !== null && typeof x === 'object') || typeof x === 'function') {
      let isCalled = false;
      try {
        let then = x.then;
        if (typeof then === 'function') {
          then.call(x, y => {
            if (isCalled) return;
            isCalled = true;
            resolvePromise(promise2, y, resolve, reject)
          }, r => {
            if (isCalled) return;
            isCalled = true;
            reject(r);
          })
        } else {
          resolve(x);
        }
      } catch (e) {
        if (isCalled) return;
        isCalled = true;
        reject(e)
      }
    } else {
      resolve(x);
    }
  }
  class Promise {
    constructor(executor) {
      this.status = PENDING;
      this.value = null;
      this.reason = null;
      this.onFulfilledCallbacks = [];
      this.onRejectedCallbacks = [];
      let resolve = value => {
        if (this.status === PENDING) {
          if (value instanceof Promise) {
            return value.then(resolve, reject);
          }
          this.status = FULFILLED;
          this.value = value;
          this.onFulfilledCallbacks.forEach(fn => fn())
        }
      }
      let reject = reason => {
        if (this.status === PENDING) {
          this.status = REJECTED;
          this.reason = reason;
          this.onRejectedCallbacks.forEach(fn => fn())
        }
      }
      try {
        executor(resolve, reject);
      } catch (e) {
        reject(e)
      }
    }
    then(onFulfilled, onRejected) {
      onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : value => value;
      onRejected = typeof onRejected == 'function' ? onRejected : err => { throw err };
      let promise2 = new Promise((resolve, reject) => {
        if (this.status === FULFILLED) {
          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.onFulfilledCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onFulfilled(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e)
              }
            })
          });
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e)
              }
            })
          });

        }
      });
      return promise2;
    }
    catch(onRejected) {
      return this.then(null, onRejected);
    }
    static all(promises) {
      return new Promise((resolve, reject) => {
        let values = [], index = 0, len = promises.length;
        promises.forEach((promise, i) => {
          Promise.resolve(promise).then((v) => {
            values[i] = v;
            ++index === len ? resolve(values) : null;
          }, reject)
        })
      })
    }
    static resolve(value) {
      return new Promise((resolve) => {
        resolve(value);
      })
    }
    static reject(value) {
      return new Promise((resolve, reject) => {
        reject(value);
      })
    }
    finally(fn) {
      return this.then(v => Promise.resolve(fn()).then(() => v), e => Promise.resolve(fn()).then(() => { throw e }));
    }
    static stop(promise) {
        let fail;
        let promise2 = new Promise((resolve, reject) => {
          fail = reject;
        })
        let race = Promise.race([promise2, promise]);
        race.reject = fail;
        return race;
      }
    static try(fn) {
        return new Promise((resolve, reject) => {
          resolve(fn())
        })
      }
    static race(promises) {
      return new Promise((resolve, reject) => {
        promises.forEach(promise => {
          Promise.resolve(promise).then(resolve, reject);
        })
      })
    }

   }
   Promise.deferred = function () {
      const dfd = {};
      dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
      })
      return dfd;
   }
   module.exports = Promise;