skip to content
headImg Xuer's Blog

了解 Promise

异步」,经常会碰到的东西,如何优雅的解决,Promise就是一个很好的选择

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

— 选自阮一峰博客 -> Promise

Promise对象有三种形态,pendingresolvedrejected。转换方式只能为 pending => resolved 或 pending => rejected。

Promise 对象用一个函数作为参数,分别处理 2 种结束状态。看以下实例。

const promise = new Promise(function(resolve, reject){
    // 类似的异步操作
    setTimeout(function(){
        if(/*成功*/){
            resolve('success');
    }, 2000)
})

promise.then(function(resolved_param){
    // success
}, function(rejected_param){
    // failed
});

1. Promise.prototype.then

用来处理 Promise 对象返回后的状态,2 个参数。 (一般只使用第一个参数,第二个参数用Promise.prototype.catch来捕获错误)

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

then 方法可以链式调用,参数是上一个 then 函数的返回值。

var promise = new Promise(function(resolve, reject){
    resolve('first')
});
promise.then(function(param){
    console.log(param);
    return 'chain';
}).then(function(str){
    console.log(str);
});
// first
// chain

2. Promise.prototype.catch

catch用来处理reject状态,是.then(null, reject(error))的别名。用来捕捉错误的回调函数。 Promise 里的错误会沿着链一直传递下去,直到遇到 reject 的回调函数,reject 的回调函数里的错误也可以让后边的 catch 捕获到。

// 写法一
var promise = new Promise(function (resolve, reject) {
 try {
  throw new Error("throw a error");
 } catch (e) {
  reject(e);
 }
});
promise.catch(function (error) {
 console.log(error);
});
// 写法二
var promise = new Promise(function (resolve, reject) {
 reject(new Error("test"));
});
promise.catch(function (error) {
 console.log(error);
});

3. Promise.all()

Promise.all()接受一个数组作为参数,将多个 promise 实例包装成新的 Promise。

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

p 的状态有两种

  1. 当 p1, p2, p3 的状态全部返回 resolved,p 的状态变为resolved,此时 p1, p2, p3 的返回值组成一个数组,传递给 p 的 resolved 回调函数;
  2. 只要 p1, p2, p3有一个状态是 rejected,p 的状态变为rejected,此时第一个被 reject 的实例的返回值,传递给 p 的 rejected 回调函数。

4. Promise.race()

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

Promise.race().all()一样,处理一个 Promise 集合,但只要 p1,p2,p3 的其中一个的状态改变,p 的状态随之变化,那个最先改变的 Promise 实例的返回值,传递给 p 的回调函数。

5. Promise.resolve()

该方法用来将一个对象转换为 Promise 对象。

var p = Promise.resolve([param]);
// 等同于
var p = new Promise(function (resolve, reject) {
 resolve([param]);
});

Promise.resolve的参数有四种情况:

(1)参数为一个 Promise 对象:

Promise.resolve()会直接返回当前 Promise 对象。

(2)参数是一个原始值

如果是原始值,Promise.resolve方法返回一个新的 Promise 对象,状态为resolved

const p = Promise.resolve("Hello");

p.then(function (s) {
 console.log(s);
});
// Hello

上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是resolved,所以回调函数会立即执行。Promise.resolve方法的参数,会同时传给回调函数。

(*3)没有参数

Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

需要注意的是,立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时触发回调函数,而不是在下一轮“事件循环”的开始时。

setTimeout(function () {
 console.log("three");
}, 0);

Promise.resolve().then(function () {
 console.log("two");
});

console.log("one");

// one
// two
// three

(4)参数是一个thenable对象

thenable对象指的是具有then方法的对象,比如下面这个对象。

let thenable = {
 then: function (resolve, reject) {
  resolve(42);
 }
};

Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

let thenable = {
 then: function (resolve, reject) {
  resolve(42);
 }
};

let p1 = Promise.resolve(thenable);
p1.then(function (value) {
 console.log(value); // 42
});

上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。

6. Promise.reject

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

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

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

上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。

注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。

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

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

上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。

未完,不知下次什么时候待续 🤔