es6极简之Promise

191 阅读5分钟

什么是promise?

异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,ES6 将其写进了语言标准

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件,通过统一api进行操作。

两个特点

(1)对象的状态不受外界影响:pending(进行中)、fulfilled(已成功)和rejected(已失败)这几种状态,外界是无法干预的

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果:如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果

优点:

1.避免了层层嵌套的回调函数

2.优于事件,事件错过就监听不到,Promise还是会返回当前确定的结果

3.统一api,流程清楚

缺点:

1.无法取消Promise,一旦新建它就会立即执行,无法中途取消

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

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

基本用法

// 创建一个
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
//使用
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

一个例子:

下面是异步加载图片的例子。

function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}

下面是一个用Promise对象实现的 Ajax 操作的例子。

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});

这个用法已经能满足90%的需求了。。。。。。

进阶篇


常用api

Promise.all()

用于需要多个请求都执行完成在操作的场景,并发执行多个请求

全成功才是成功(p1, p2, p3必须是promise对象

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

Promise.race()

有一个成功就是成功(p1, p2, p3必须是promise对象)

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数

Promise.allSettled()

同上面的两个

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

Promise.any()

Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。该方法

all() race() allSettled() any() 总结

相同点:

1.这四个方法都是接受一组 Promise 实例(ps:可以不是,这个先不讲)作为参数,包装成一个新的 Promise 实例

2.并发执行

不同点:

all()

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数(应该是catch)(ps:如果 p1p2p3自身有.catch(e => e);   那么久不会执行 p 的catch,而是执行p的then方法

race() 

只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。(示例:如果 5 秒之内fetch方法无法返回结果)

allSettled()

等到三个请求都结束,不管请求成功还是失败,最后的状态总是fulfilled

any()

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态

Promise.prototype.then()

基础的用法,不解释

Promise.prototype.finally()

无论如何都会执行的函数

异常处理

Promise.prototype.catch() 

要点总结:上一个错误总是会被下一个catch捕获,总是使用catch,Promise内部错误即使没有捕获也不会终止程序()


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


1Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

2一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

3跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。不会退出进程、终止脚本执行,通俗的说法就是“Promise 会吃掉错误”。

4代码运行完catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。如果没有报错,则会跳过catch方法