Skip to content
imshengli blog
Go back

ES6 Promise

总结

Promise 是一种异步解决方案, 在异步流程中,如果想在未来某一时刻执行某项操作, 可以通过回调或事件的方式处理, 但存在嵌套过深的场景。

通过Promise,可以通过链式链路进行操作, 简化了异步操作的控制; ES6 提供了原生的解决方案。

如果自己完成一个Promise解决方案, 应该如何去处理呢?

简介

异步编程的一种解决方案。 ES6统一了用法,原生提供了Promise对象。

特点:

基本用法

// Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
  // 会继续执行这行代码
  // 但最好不要这样做,在 then 里执行就好了;
  console.log(2);
});
// then 接受两个回调函数作为参数:
// 分别指定resolved状态和rejected状态的回调函数。
// 第二个方法可选
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

异步加载图片

function loadImageAsync(url) {
  // 返回一个 Promise 实例
  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.prototype.then()

添加状态改变时的回调函数。

then方法返回的是一个新的Promise实例

如果返回的是一个 Promise 实例, 会等待该Promise对象的状态发生变化,才会被调用。

loadImageAsync('https://imshengli.com/images/logo.png')
.then(image => {
  console.log(image);
  return image;
}).then(res => {
  console.log(res);
});

Promise.prototype.catch()

.then(null, rejection)的别名, 用于指定发生错误时的回调函数, 返回的还是 Promise 对象。

then方法指定的回调函数,如果运行中抛出错误, 也会被catch方法捕获。

Promise 对象的错误具有“冒泡”性质, 会一直向后传递,直到被捕获为止。

建议总是使用catch方法,而不使用then方法的第二个参数。

跟传统的try/catch代码块不同的是, 如果没有使用catch方法指定错误处理的回调函数, Promise 对象内部抛出的错误不会传递到外层代码, 即不会有任何反应。

// 不会退出进程、终止脚本执行,
const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};
someAsyncThing().then(function() {
  console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123

Promise.all()

将多个 Promise 实例,包装成一个新的 Promise 实例。

// 参数可以不是数组,但必须具有 Iterator 接口,
// 且返回的每个成员都是 Promise 实例;
// 如果不是,就会先调用的Promise.resolve方法,
// 将参数转为 Promise 实例。
const p = Promise.all([p1, p2, p3]);
// 都变成fulfilled,p的状态才会变成fulfilled,
// 有一个被rejected,p的状态就变成rejected。

// 如果 作为参数的 Promise 实例,
// 自己定义了catch方法,那么它一旦被rejected,
// 并不会触发Promise.all()的catch方法。

Promise.race()

将多个 Promise 实例,包装成一个新的 Promise 实例。

// 只要p1、p2、p3之中有一个实例率先改变状态,
// p的状态就跟着改变。
// 那个率先改变的 Promise 实例的返回值,
// 就传递给p的回调函数。
const p = Promise.race([p1, p2, p3]);

Promise.resolve()

将现有对象转为 Promise 对象。

// 将 jQuery 生成的deferred对象,转为一个新的 Promise 对象。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));

参数:

注意:

立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时, 而不是在下一轮“事件循环”的开始时。 setTimeout(fn, 0)在下一轮“事件循环”开始时执行。

Promise.reject()

返回一个新的 Promise 实例,该实例的状态为rejected。

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});
// 出错了

Promise.reject()方法的参数, 会原封不动地作为reject的理由,变成后续方法的参数。

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

done()

自行添加: 总是处于回调链的尾端,保证抛出任何可能出现的错误。

实现:

Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 抛出一个全局错误
      setTimeout(() => { throw reason }, 0);
    });
};

finally()

自行添加: 不管 Promise 对象最后状态如何,都会执行的操作。

实现:

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

Promise.try()

自行添加: 不知道或者不想区分,函数f是同步函数还是异步操作, 但是想用 Promise 来处理它。

// 缺点,就是如果f是同步函数,
// 那么它会在本轮事件循环的末尾执行。
Promise.resolve().then(f);

改进:

const f = () => console.log('now');
(
  () => new Promise(
    resolve => resolve(f())
  )
)();
console.log('next');
// now
// next

参考文档


Share this post on:

Previous Post
JS 字符串
Next Post
JS 正则表达式