ES6中的promise,带你真正弄懂promise的各种方法的用法

2,803 阅读7分钟

这是我参与11月更文挑战的第七天,活动详情请查看:2021最后一次更文挑战

1.含义

promise 是异步编程的一种解决方案,promise对象是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从他获取异步操作的消息。

1)promise 对象的特点:

  • 对象的状态不受外界的影响。promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功) 和 rejected(已失败);
  • 一旦状态改变,就不会再改变,任何时候都可以得到这个结果;

2)缺点:

  • 无法取消promise,一旦新建他就会立即执行,无法中途取消
  • 如果不设置回调函数,promise内部抛出的错误,不会反应到外部
  • 当处于pending状态时,无法得知目前的状态是刚刚开始还是即将完成

2.基本用法

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

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

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});


// 理解:promise构造函数接受俩个参数,分别是resolve 和reject。
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

// promise 实例生成后,可以使用then 方法 分别指定 resolved状态 和reject 状态的回调函数。

1)promise 新建后就会立即执行

let promise = new Promise(function (resolve,reject) {
        console.log("1,promise");
        resolve();
    })
    promise.then(function () {
        console.log('3,resolved.')
    })
    console.log("2,hello")


 //  promise 新建之后立即就会执行---所以就会立即执行 输出 1,promise
 //  then  方法指定的回调函数,将在当前脚本所有任务同步执行完成之后才会执行----输出 在 2 hello 之前按照顺序执行 和 1,hello
 //  当当前脚本执行完毕之后,then里的内容才会执行 ----  输出 3,resolved

2) 调用 resolve或者reject 并不会终结 promise 的参数函数的执行。

new Promise((resolve, reject) => {
  resolve(1);
   return  resolve(1);  // 加上这行话 就不会在输出2
  console.log(2);
}).then(r => {
  console.log(r);
});
// 因为立即 resolved 的Promise 是在本轮事件循环的末尾执行,总是晚于本轮事件循环的同步任务。所以先输出2后输出1的内容

更好的写法:
new Promise((resolve, reject) => {
   return  resolve(1);  
}).then(r => {
  console.log(2);
  console.log(r);
});
输出的结果
//2
//1

3. Promise.prototype.then()

then方法是定义在原型对象Promise.prototype上的,为Promise 实例添加状态改变时的回调函数。 then方法返回一个新的promise实例,注意不是原来的Promise实例

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function (comments) {
  console.log("resolved: ", comments);
}, function (err){
  console.log("rejected: ", err);
});



// 等价写成箭头函数的写法
getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err)
);

4.Promise.prototype.catch()

1、含义

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

catch 捕获promise 抛出的异常的状态rejected或者是then 方法指定的回调函数在运行中抛出的错误,也会被catch方法捕获。

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test

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

// 写法二
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});

2、catch 返回一个promise对象,同时还可以继续调用then方法

 const someAsyncThing = function () {
        return new Promise(function (resolve,reject) {
            // 因为 x 没有被声明定义,所以会报错
           // let x = 5;  // 如果声明了,则不会报错
            resolve(x +2);
        })
    }
    //调用someAsyncThing
    someAsyncThing().catch(function (error) {
        console.log('no no no',error)
    }).then(function () {
        console.log('carry on');
    })
// 因为 x 没有被声明定义,所以会报错 在运行完catch()方法指定的回调函数之后,会接着
// 运行后面的then()方法指定的回调函数。如果没有报错,则会跳过catch()方法。

5.promise.prototype.finally()

1、含义

  • finally()方法用于指定不管promise 对象最后的状态 如何,都会执行的操作。
  • finally 方法的回调函数不接受任何参数,所以finally方法里面的操作和promise 返回的状态无关,不依赖于promise的执行结果。

2、finally 本质是then方法的特例。

promise
.finally(() => {
  // 语句
});

// 等同于
promise
.then(
  result => {
    // 语句
    return result;
  },
  error => {
    // 语句
    throw error;
  }
);


// 说明:如果不使用finally方法,同样的语句需要为成功和失败俩种情况各写一次。有了finally方法,则只需要写一次。

6.promise.all()

1.含义

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

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

promise.all() 方法接受一个数组作为参数,p1、p2、p3 都是他的实例。如果参数不是数组的话,但必须有iterator 的接口,且返回的每一个成员都是promise 实例。

p的状态由p1p2p3决定,分成两种情况。

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

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    const p1 = new Promise((resolve, reject) => {
        resolve('hello');
    })
        .then(result => result)
        .catch(e => e);

    const p2 = new Promise((resolve, reject) => {
        throw new Error('报错了');
    })
        .then(result => result)
        .catch(e => e);

    Promise.all([p1, p2])
        .then(result => console.log(result))
        .catch(e => console.log(e));
    // ["hello", Error: 报错了] ---- 这是rejected 输出的语句
    // Array(2)  0:hello 1:Error: 报错了   --- 这是resolv的执行结果

说明:上面代码中,p1resolvedp2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。

7.总结

  • all() :是数组中的所有的对象变为resolve 只要有一个状态变为reject 则执行 all() 方法里的内容。
  • race():只要有一个实例发生改变时,P的总数组的状态就会随之改变。
  • allSettled():只有等待这些参数的实例都返回的结果,不管是fulfilled 还是rejected,包装实例才会结束。
  • any():只要参数所有的实例都变成fulfilled状态,包装的实例就会变成rejected 状态。如果所有的参数实例都变成rejected状态。包装实例就会变成rejected状态。

8.promise.resolve()

需要将现有的对象转为promise 对象,promise.resolve() 方法就起到这个作用。

1、参数是一个Promise 实例

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

2、参数是一个thenable 对象

thenable对象指的是具有then方法的对象。

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

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

 // 总结:promise.resolve()方法会将这个对象转为promise 对象,然后立即执行thenable 对象的then 方法
    // thenable 对象的then()方法执行后,对象P1状态就变为resolved。从而立即执行最后那个then方法指定的回调函数
     // 最后输出的结果为42

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

const p = Promise.resolve('Hello'); // 字符串对象不具有then() 方法

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

如果参数是一个原始值,或者是一个不具有then() 方法的对象,则Promise.resolve()方法返回一个新的Promise对象(从一生成的状态就是resolved,所以回调函数会立即执行),状态为resolved。

4、不带有任何参数

注:立即resolve()的promise 对象,是在本轮事件循环的结束时执行,而不是在下一轮的事件循环的开始时执行。

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

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

console.log('one');

// one
// two
// three

setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。

最后

文章到此为止了,如果有不足之处,欢迎大家评论指出。

附件

附上代码的连接地址,帮助初学的小伙伴有一个更深的认识,打开控制台就可以看到输出结果。 项目代码地址