Promise

276 阅读9分钟

Promise

Promise是异步编程的一种解决方案,所谓的Promise,简单来说就是一个容器,里面保存着未来才会结束的事件的结果。

Promise对象有以下两个特点:

1.对象的状态不受外界影响。Promise对象代表一种异步操作,有三个状态 PENDING(进行中)、FULFILLED(已成功)、REJECTED(已失败) 只有异步操作的结果可以改变状态,其他的任何操作都不能改变状态。

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

  • MyPromise接收一个函数executorexecutor有两个参数resolve方法和reject方法
  • resolvePENDING改变为FULFILLED
  • rejectPENDING改变为REJECTED
  • promise变为FULFILLED状态后具有一个唯一的value
  • promise变为REJECTED状态后具有一个唯一的reason

只要这两种情况发生了,状态就不会再改变了,并且会一直保持这个结果。这与事件监听不同,事件的特点是,不同时间监听,得到的结果都是不同的。

Promise 规范有很多,如Promise/APromise/BPromise/D 以及 Promise/A 的升级版 Promise/A+ES6中采用了 Promise/A+ 规范

promise 采用了观察者模式 用特定方式 注册 对应 状态 的事件处理函数

Promise/A+并未规范raceallcatch方法,这些是ES6自己规范的。

finally 是babel 转es5 加的。

当然Promise也有一些缺点:

首先无法取消Promise,一旦创建他就会立即执行,中途无法取消,除非包装一层,返回一个闭包异步操作结果要改变状态的时候根据取消的参数决定是否改变

其次,如果还不设置回调函数,Promise内部跑出的错误,不会反映到外部。

最后,当处于Pending状态时,无法得知目前进展到哪一个阶段了。

二、基本用法

Promise对象是一个构造函数,用来生成Promise实例的。

 const promise = new Promise(function(resolve, reject){
     ...
     if(/* 异步操作成功 */){
       resolve(value)
     } else {
        reject(error);
     }
  })

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。他们是两个函数,由js引擎提供,不需要自己部署

1.resolve函数的作用是: 将Promise对象的状态从将PENDING改变为FULFILLED,在异步操作成功时调用,并将一步操作的结果,作为参数传递出去。

2 reject函数的作用是:

将Promise对象的状态从“未成功”变成“失败”(Pending->REJECTED),在异步操作失败时候调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定FULFILLED状态和REJECTED状态的回调函数

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

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为FULFILLED时调用,第二个回调函数是Promise对象的状态变成Rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

这里我们就可以用Promise来异步加载图片:

 function loadImageAsync(url) {
   return new Promise(function(resolve, reject) {
         const image = new Image();
         image.onload = function() {
           resolve(image);
         }
         imgage.onerror = function() {
           reject(new Error())
         }
         image.src  = url;
   })
 }

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    getJSON("/posts.json").then(function(json) {
    return json.post;
    }).then(function(post) {
    // ...
    });

这一段代码中使用了then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数中。

三、Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数

    getJSON("/posts.json").then(function(posts) {
    // ...
    }).catch(function(error) {
    // 处理 getJSON 和 前一个回调函数运行时发生的错误
    console.log('发生错误!', error);
    });

四、Promise.all()

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。

var p = Promise.all([p1, p2, p3]);

当p1p2p3的状态都是FULFILLED的时候,p的状态才是FULFILLED,否则只要有一个是rejected那么p的状态就时rejected。 里面是空数组的时候,promise.all立即决议为成功

五、Promise.race()

Promise.race()方法同样是将多个Promise实例,包装成一个新的Promise实例,用法和all是一样的。

它与all的不同在于:race是只要有一个状态改变了,p的状态就会改变,并且变成和第一个状态改变之后的一样的状态。

六、Promise.resolve()

有时候需要将现有的对象转为Promise对象,Promise.resolve方法就起到这个作用。

 Promise.resolve('foo')
 // 等价于new Promise(resolve => resolve('foo'))

Promise.resolve方法的参数分为四种情况:

1.参数是一个Promise实例

如果参数是Promise实例,那么Promise.resolve将不做任何修改、原封不动地返 回这个实例。

let poruomiesi = new Promise(resolve => {
    resolve('耶!');
});

 //直接返回传递进去的promise
let p = Promise.resolve(poruomiesi);//p === poruomiesi

p.then(data => void console.log(data));

2.参数是一个thenable对象

thenable对象指的是具有then方法的对象,比如下面这个对象。 Promise.resolve方法会将这个对象转为Promise对象,然后立即执行thenable对象的then方法。

let obj = {
  then(cb) {
    console.log('执行then');
    cb('cb');
  }
}
// 立即执行then方法
Promise.resolve(obj).then(data => {
  console.log(data);
})

3.参数不是具有then方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve 方法返回一个新的Promise对象,状态为FULFILLED

let p1 = new Promise(resolve => {
   resolve('成功!');
});

let p2 = Promise.resolve('成功!');
//注:p1和p2是等价的

4、Promise.resolve方法允许调用时不带参数,直接返回一个Resolved状态的Promise对象。 所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve方法

七、Promise.reject()

Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。它的参数用法与Promise.resolve方法完全一样。

八、其他两个附加方法

1.done()方法

Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。 因此,我们可以提供一个done方法,总是处于回调链的尾端保证抛出任何可能出现的错误

asyncFunc().then(f1).catch(f2).then(f3).done(f4);


done 源码:

Promise.prototype.done = function(onFulilled, onRejexted) {
  this.then(onFulfilled, onRejected).catch(function(reason){setTimeout(()=>{
    throw reason
  }, 0);})
} 

从上面代码可见,done方法的使用,可以像then方法那样用,提供fulfilledrejected状态的回调函数,也可以不提供任何参数。但不管怎样,done都会捕捉到任何可能出现的错误,并向全局抛出.

2.finally()方法

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法最大的区别就在于,它接受一个普通的回调函数作为参数,该参数不管怎样都必须执行。 举个例子 服务器处理Promise 请求, 然后使用过finally 方法关掉服务器。

server.listen(0).then(function(){// run test}).finally(server.stop);

源码:

  Promise.prototype.finally = function(callback) {
    let P = this.constructor
    return this.then(
      value => P.resolve(callback()).then(()=> value),
      reason => P.resolve(callback()).then(()=> reason)
    )
  }

promise A+

基础版本

  • 设定三个状态 PENDING、FULFILLED、REJECTED ,只能由PENDING改变为FULFILLED、REJECTED,并且只能改变一次
  • MyPromise接收一个函数executorexecutor有两个参数resolve方法和reject方法
  • resolvePENDING改变为FULFILLED
  • rejectPENDING改变为FULFILLED
  • promise变为FULFILLED状态后具有一个唯一的value
  • promise变为REJECTED状态后具有一个唯一的reason
    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';

    function MyPromise(executor) {
      this.state = PENDING;
      this.value = null;
      this.reason = null;

      const resolve = (value) => {
        if (this.state === PENDING) {
          this.state = FULFILLED;
          this.value = value;
        }
      }

      const reject = (reason) => {
        if (this.state === PENDING) {
          this.state = REJECTED;
          this.reason = reason;
        }
      }

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

then方法

  • then方法接受两个参数onFulfilled、onRejected,它们分别在状态由PENDING改变为FULFILLED、REJECTED后调用
  • 一个promise可绑定多个then方法
  • then方法可以同步调用也可以异步调用
  • 同步调用:状态已经改变,直接调用onFulfilled方法
  • 异步调用:状态还是PENDING,将onFulfilled、onRejected分别加入两个函数数组onFulfilledCallbacks、onRejectedCallbacks,当异步调用resolvereject时,将两个数组中绑定的事件循环执行。
  function MyPromise(executor) {
      this.state = PENDING;
      this.value = null;
      this.reason = null;
      this.onFulfilledCallbacks = [];
      this.onRejectedCallbacks = [];

      const resolve = (value) => {
        if (this.state === PENDING) {
          this.state = FULFILLED;
          this.value = value;
          this.onFulfilledCallbacks.forEach(fun => {
            fun();
          });
        }
      }

      const reject = (reason) => {
        if (this.state === PENDING) {
          this.state = REJECTED;
          this.reason = reason;
          this.onRejectedCallbacks.forEach(fun => {
            fun();
          });
        }
      }

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

    MyPromise.prototype.then = function (onFulfilled, onRejected) {
      switch (this.state) {
        case FULFILLED:
          onFulfilled(this.value);
          break;
        case REJECTED:
          onFulfilled(this.value);
          break;
        case PENDING:
          this.onFulfilledCallbacks.push(() => {
            onFulfilled(this.value);
          })
          this.onRejectedCallbacks.push(() => {
            onRejected(this.reason);
          })
          break;
      }
    }

then方法异步调用

如下面的代码:输入顺序是:1、2、ConardLi

console.log(1);

let promise = new Promise((resolve, reject) => {
  resolve('ConardLi');
});

promise.then((value) => {
  console.log(value);
});

console.log(2);

虽然resolve是同步执行的,我们必须保证then是异步调用的,我们用settimeout来模拟异步调用(并不能实现微任务和宏任务的执行机制,只是保证异步调用)

    MyPromise.prototype.then = function (onFulfilled, onRejected) {
      if (typeof onFulfilled != 'function') {
        onFulfilled = function (value) {
          return value;
        }
      }
      if (typeof onRejected != 'function') {
        onRejected = function (reason) {
          throw reason;
        }
      }
      switch (this.state) {
        case FULFILLED:
          setTimeout(() => {
            onFulfilled(this.value);
          }, 0);
          break;
        case REJECTED:
          setTimeout(() => {
            onRejected(this.reason);
          }, 0);
          break;
        case PENDING:
          this.onFulfilledCallbacks.push(() => {
            setTimeout(() => {
              onFulfilled(this.value);
            }, 0);
          })
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              onRejected(this.reason);
            }, 0);
          })
          break;
      }
    }

then方法链式调用

保证链式调用,即then方法中要返回一个新的promise,并将then方法的返回值进行resolve

注意:这种实现并不能保证then方法中返回一个新的promise,只能保证链式调用。

    MyPromise.prototype.then = function (onFulfilled, onRejected) {
      if (typeof onFulfilled != 'function') {
        onFulfilled = function (value) {
          return value;
        }
      }
      if (typeof onRejected != 'function') {
        onRejected = function (reason) {
          throw reason;
        }
      }
      const promise2 = new MyPromise((resolve, reject) => {
        switch (this.state) {
          case FULFILLED:
            setTimeout(() => {
              try {
                const x = onFulfilled(this.value);
                resolve(x);
              } catch (reason) {
                reject(reason);
              }
            }, 0);
            break;
          case REJECTED:
            setTimeout(() => {
              try {
                const x = onRejected(this.reason);
                resolve(x);
              } catch (reason) {
                reject(reason);
              }
            }, 0);
            break;
          case PENDING:
            this.onFulfilledCallbacks.push(() => {
              setTimeout(() => {
                try {
                  const x = onFulfilled(this.value);
                  resolve(x);
                } catch (reason) {
                  reject(reason);
                }
              }, 0);
            })
            this.onRejectedCallbacks.push(() => {
              setTimeout(() => {
                try {
                  const x = onRejected(this.reason);
                  resolve(x);
                } catch (reason) {
                  reject(reason);
                }
              }, 0);
            })
            break;
        }
      })
      return promise2;
    }

catch方法

若上面没有定义reject方法,所有的异常会走向catch方法:

MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

finally方法

不管是resolve还是reject都会调用finally

MyPromise.prototype.finally = function(fn) {
    return this.then(value => {
       fn();
       return value;
    }, reason => {
        fn();
        throw reason;
    });
};

Promise.resolve

Promise.resolve用来生成一个直接处于FULFILLED状态的Promise。

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

Promise.reject

Promise.reject用来生成一个直接处于REJECTED状态的Promise。

MyPromise.reject = function(reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason);
  });
};

all方法

接受一个promise数组,当所有promise状态resolve后,执行resolve

    MyPromise.all = function (promises) {
      return new Promise((resolve, reject) => {
        if (promises.length === 0) {
          resolve([]);
        } else {
          let result = [];
          let index = 0;
          for (let i = 0; i < promises.length; i++) {
            promises[i].then(data => {
              result[i] = data;
              if (++index === promises.length) {
                resolve(result);
              }
            }, err => {
              reject(err);
              return;
            });
          }
        }
      });
    }

race方法

接受一个promise数组,当有一个promise状态resolve后,执行resolve

    MyPromise.race = function (promises) {
      return new Promise((resolve, reject) => {
        if (promises.length === 0) {
          resolve();
        } else {
          let index = 0;
          for (let i = 0; i < promises.length; i++) {
            promises[i].then(data => {
              resolve(data);
            }, err => {
              reject(err);
              return;
            });
          }
        }
      });
    }