JS笔记《Promise》

61 阅读8分钟

概述

  • Promise是异步编程的一种解决方案,比传统的回调函数和事件更合理。简单来说就是一个容器,里面保存着未来才会结束的事件,通常是一个异步操作。Promise对象有两个特点:

    • 对象的状态不受外界影响,Promise对象代表一个异步操作,有三种状态:pending(进行中)fulfilled(已成功)rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态。
    • 一旦状态改变就不会在变。任何时候都可以得到这个结果。Promise对象的状态改变只有两种可能,从pendingfulfilled;从pendingrejected。只要这两种情况发生,状态就凝固了,不会再变。
  • 有了Promise对象,就可以将异步操作已同步操作的流程表达出来,避免了层层嵌套的回调函数。但是Promise也有一些缺点,如下:

    • 无法取消。一旦新建它就会立即执行,无法中途取消。
    • 如果不设置回调函数,Promise内部发生的错误不会反映到外部。
    • 当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成。

构造函数

  • Promise对象是一个构造函数,用来生成Promise实例。接受一个函数作为参数,该函数的两个参数分别是resolvereject,它们也是两个函数。
  • resolve函数的作用是,将Promise对象的状态从pending(未完成)变为fulfilled(成功)。在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
  • reject函数的作用是,将Promise对象的状态从pending(未完成)变为rejected(失败)。在异步操作失败时调用,并将异步操作的错误作为参数传递出去。
const promise = new Promise((resolve, reject) => {
 if(success){
    resolve(data);
 }else{
    reject(error);
 }
})
  • promise新建后会立即执行(同步)。
console.log(1)

// promise的参数的函数是同步执行的
const p = new Promise((resolve, reject) => {
  console.log(2);
  resolve('abc')  // resolve/reject 会在微任务中执行。所以是异步的
  console.log(3); // resolve或reject并不会终结下面代码的执行
});


p.then(res => {
  console.log(res)
})

console.log(4)

// 1 2 3 4 abc

then()

  • Promise实例生成后,可以用then方法分别指定fulfilldedrejected状态的回调函数。then方法接受两个回调函数作为参数,第一个回调函数是Promise对象状态变为resolve时调用。第二个回调函数是Promise对象状态变为rejected时调用。这两个参数都是可选的,都可以接受Promise对象传出的值作为参数。
const promise = new Promise((resolve, reject) => {
  resolve(1) // 将promise对象状态修改为fulfilled,并将参数传递出去
})

// then方法是监听Promise状态变为fulfilled和rejected的回调函数
promise.then(res => {   // fulfillded状态回调函数 
  console.log(res)  // 1
}, err => {  // rejected状态回调函数

})
  • then方法返回的是一个新的Promise实例。因此可以采用链式调用。then返回的promise实例的状态是由then内部回调函数的执行结果来决定的,如果回调函数返回结果是非Promise类型(包括没有返回值),则then方法返回的promise对象的状态为fulfilled
p.then(res => {
   return 123;   //当前这个then返回的promise状态为fulfilled,值为123
})
.then(result => {  // 接收到了上一个promise状态的成功回调,可以执行
   console.log(123)
})
  • 如果then回调函数的返回结果是promise对象,则then方法返回的promise对象的状态由内部的promise对象决定。
p.then(res => {
  return new Promise((resolve, reject) => {
    reject('error'); // then返回的promise对象状态修改为了rejected
  });
})
.then(result => {
  console.log(result)
}, err => {  // 上一个promise状态为rejected,所以rejected回调函数执行
  console.log(err)   // error
})

catch()

  • 如果promise对象的状态变为rejected,就会调用此方法指定的回调函数。功能等同于then方法的第二个回调函数。另外,如果then的第一个回调函数执行中抛出错误,也会被此方法监听。返回的还是一个Promise对象。
const p = new Promise((resolve, reject) => {
    reject('error');  // 状态变为rejected,触发catch方法
    // throw new Error('error1');  // 与 reject一致
})

p.then(res => {
  console.log(res);
}).catch(err => {
  console.log(err);  // error
})
  • Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止。

finally()

  • 不管Promise对象最后状态如何,都会执行的操作。回调函数不接受任何参数,也就意味着不知道前面promise对象的状态是什么,所以finally()中的操作,应该是与状态无关的,不依赖promise的执行结果。
const p = new Promise((resolve, reject) => {
   reject('error')
})

p.then(res => {
  throw new Error('error2')
}).catch(err => {
  console.log(err)   // error
}).finally(() => {
  console.log('finally')  // finally
})

Promise.all()

  • 用于将多个Promise实例包装成一个新的Promise实例。接受一个数组作为参数,数组成员都是promise实例。新的promise实例对象的状态由数组成员决定:

    • 只有成员状态都是fulfilled,这个新的promise实例的状态才会变成fulfilled,将promise成员的返回值组成一个数组,传递给then
    • 如若数组成员其中一个状态为rejected,这个新的promise实例状态就变为了rejected,此时第一个被rejected的数组成员的返回值会被传递给catch(如果成员自己内部定义了catch方法,就不会触发外部的catch)。
// p1状态为成功
const p1 = new Promise((resolve, reject) => {
    resolve('hello');
  })
  .then(result => result)
  .catch(e => e);

// p2状态为失败,但是自己定义了catch
const p2 = new Promise((resolve, reject) => {
    throw new Error('报错了');
  })
  .then(result => result)
  .catch(e => e);

Promise.all([p1, p2])
  .then(result => {     // p2执行完catch后,变成了成功状态,导致这两个promise都是成功
    console.log(result) // ['hello', Error: 报错了]
  })
  .catch(e => {

  });

Promise.race()

  • 将多个Promise实例包装成一个新的Promise实例。接受一个数组作为参数,数组成员都是promise实例。只要数组成员中有一个率先改变了状态,这个新的promise就会跟着改变。那个改变了状态的数组成员的返回值会传递到后续的回调函数中处理。
// 5秒后状态改为失败
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('error');
  }, 5000);
})

// 3秒后状态改为成功
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('data');
  }, 3000);
})

Promise.race([p1, p2]).then(res => {
  console.log(res)  // data  成功!
}).catch(err => {
  console.log(err);
})

Promise.allSettled()

  • 将多个Promise实例包装成一个新的Promise实例。接受一个数组作为参数,数组成员都是promise实例。只有等到所有数组成员都发生状态变更(不管是fulfilled还是rejected),这个新的promise才会跟着改变,且状态总是fulfilled,不会变成rejected。它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('error');
  }, 5000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('data');
  }, 3000);
})

const allP = Promise.allSettled([p1, p2]);
allP.then(res => {
  console.log(res)
  //  [
  //     {status: 'rejected', reason: 'error'},  第一个promise失败了,reason是error
  //     {status: 'fulfilled', value: 'data'}    第二个promise成功了,value是data
  //  ]
})

Promise.any()

  • 将多个Promise实例包装成一个新的Promise实例。接受一个数组作为参数,数组成员都是promise实例。只要数组成员有一个变成fulfilled状态,这个新的promise实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,这个新的promise实例就会变成rejected状态。
  • race方法的区别是,any不会因为某个成员的状态变为rejected就结束,必须等到所有成员全都变成了rejected才会结束。
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('error');
  }, 5000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('data');
  }, 3000);
})

const allP = Promise.any([p1, p2]);
allP.then(res => {
  console.log(res);  // data  一个成功一个失败,所以状态为成功
}).catch(err => {
  console.log(err);
})
  • 如果成员状态全是rejectedany抛出的错误是一个AggregateError实例对象。
const p1 = new Promise((resolve, reject) => {
  throw Error('error1')
})

const p2 = new Promise((resolve, reject) => {
  throw Error('error2')
})

const allP = Promise.any([p1, p2]);
allP.then(res => {
  console.log(res);  
}).catch(err => {
  console.log(typeof err);  // Object
  console.log(err);         // AggregateError: All promises were rejected
  
  // errors属性是一个数组,包含了所有成员的错误
  console.log(err.errors);  // [Error: error1, Error: error2]
})

Promise.resolve()

  • 将现有对象转为Promise对象,参数分为以下四种情况:

    • 参数是一个promise实例:将原封不动的返回。
    • 参数是一个thenable对象(具有then方法的对象):会将这个对象转为promise对象,然后立即执行这个对象的then方法。
    let thenable = {
      then: function (resolve, reject) {
        resolve(42);
        // reject('error');  如果执行的是reject,promise就是rejected状态
      }
    };
    
    let p1 = Promise.resolve(thenable);
    p1.then(function (value) {
      console.log(value); // 42
    });
    
    • 参数是不具有then方法的对象或者不是对象:返回一个新的promise对象,状态为fulfilled
    const p = Promise.resolve('Hello');
    p.then(s => {
      console.log(s)
    });
    
    • 不带有参数:直接返回一个fulfilled状态的promise对象。

Promise.reject()

  • Promise.resolve不一样,此方法返回一个新的 Promise 实例,该实例的状态为rejected。参数会原封不动地作为reject的理由,变成后续方法的参数。
Promise.reject('出错了')
  .catch(e => {
    console.log(e)  // 出错了
  })