Promise使用笔记

PS:本文是对参考资料1的阅读笔记,可以稍微浏览下,建议还是看参考资料1。

constructor()

  • Promise在构造函数传入的函数里(通过resolve或reject)决定状态,在then()函数里注册确定状态后的回调。

  • 如果在resolve里传入一个新的promise,那么当前promise的状态就不由resolve的调用决定,而是由传入的promise的状态决定。

    const p1 = new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('fail')), 3000)
    })
    
    const p2 = new Promise(function (resolve, reject) {
        setTimeout(() => resolve(p1), 1000)
    })
    
    p2
        .then(result => console.log(result))
        .catch(error => console.log(error))
    // Error: fail
    
  • resolve和reject不会终结Promise()的参数函数的执行。

    new Promise((resolve, reject) => {
      resolve(1);
      console.log(2);
    }).then(r => {
      console.log(r);
    });
    // 2
    // 1
    

then()

  • then()的作用是为Promise实例添加状态改变时的回调函数。

  • then()返回的是一个新的Promise实例(不是原来那个Promise实例),因此可以采用链式写法。

    getJSON("/posts.json")
    	// 第一个回调函数完成以后,会将返回结果作为参数传入第二个回调函数。
        .then(function(json) {
        	return json.post;
    	})
        .then(function(post) {
        	// ...
    	});
    
  • 多次调用then()形成了一棵“promise树”,树上的promise是以广度优先遍历的顺序来调用的。

    const p1 = new Promise((resolve, reject)=>{console.log(1);resolve()})
      const p11 = p1.then(()=>{console.log(11)})
        const p111 = p11.then(()=>{console.log(111)})
        const p112 = p11.then(()=>{console.log(112)})
      const p12 = p1.then(()=>{console.log(12)})
        const p121 = p12.then(()=>{console.log(121)})
        const p122 = p12.then(()=>{console.log(122)})
    

    输出:

    1
    11
    12
    111
    112
    121
    122
    

catch()

  • catch()是then(null, onRejected)或then(undefined, onRejected)的别名,用于指定发生错误时的回调函数。

    getJSON('/posts.json').then(function(posts) {
      // ...
    }).catch(function(error) {
      // 处理 getJSON 和 前一个回调函数运行时发生的错误
      console.log('发生错误!', error);
    });
    
  • catch()不能像then()一样链式调用,但可以在一个promise上使用多次catch()注册多个onRejected()。

    const p1 = new Promise((resolve, reject)=>{console.log(1);reject()})
      const p11 = p1.catch(()=>{console.log(11)})
        const p111 = p11.catch(()=>{console.log(111)})
        const p112 = p11.catch(()=>{console.log(112)})
      const p12 = p1.catch(()=>{console.log(12)})
        const p121 = p12.catch(()=>{console.log(121)})
        const p122 = p12.catch(()=>{console.log(122)})
    

    输出:

    1
    11
    12
    
  • catch()的链式调用得在catch()中手动抛出异常。

    new Promise((resolve, reject) => {
      console.log(1);
      throw new Error('error1')
    })
      .catch((err) => {
        console.log(2);
        throw err;
      })
      .catch((err) => {
        console.log(3);
      })
    

    输出:

    1
    2
    3
    
  • 如果Promise状态已经变成了resolved,再抛出错误是无效的:

    const promise = new Promise(function(resolve, reject) {
        resolve('ok');
        throw new Error('test');
    });
    promise
        .then(function(value) { console.log(value) })
        .catch(function(error) { console.log(error) });
    // ok
    
  • Promise内部产生的异常不会被抛到外部(这意味着外部的代码可以正常运行,并且外部的try catch捕获不到Promise内部的异常),会终止Promise继续运行,并在内部直接输出异常信息。

    try {
        new Promise((resolve, reject)=>{
            reject(new Error('test'))
        })
        .then(()=>{
            console.log('then') // 执行不到该语句
        })
    } catch (e) {
        console.log('try catch is executed') // 执行不到该语句
    }
    setTimeout(() => { console.log('out scripts is running') }, 2000);
    

    输出:

    (node:14976) UnhandledPromiseRejectionWarning: Error: test
    ...
    (node:14976) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
    (node:14976) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
    out scripts is running
    

    在最后一个catch()中抛出的异常统一不会被抛到外部。

    try {
      new Promise((resolve, reject) => {
        reject(new Error('test'))
      })
        .then(() => {
          console.log('then') // 执行不到该语句
        })
        .catch((err) => {
          console.log('catch');
          throw err
        })
    } catch (e) {
      console.log('try catch is executed') // 执行不到该语句
    }
    setTimeout(() => { console.log('out scripts is running') }, 2000);
    

    输出:

    catch
    (node:8524) UnhandledPromiseRejectionWarning: Error: test
    ...
    (node:8524) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
    (node:8524) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
    out scripts is running
    
  • 下面代码中,Promise 指定在下一轮“事件循环”再抛出错误。到了那个时候,Promise 的运行已经结束了,所以这个错误是在 Promise 函数体外抛出的,会冒泡到最外层,成了未捕获的错误。因为是在下一个事件循环抛出错误,因此当前的try catch也无法捕获。

    try {
        const promise = new Promise(function (resolve, reject) {
            setTimeout(function () { throw new Error('test') }, 0)
        });
    } catch (err) {
        console.log('catch');
    }
    

    输出:

    Error: test
    

finally()

finally()是不管状态如何,最终都会执行的操作。该操作在ES2018引入。

finally()传入的方法不接收任何参数。

finally()是then()的特例,只是then()要分别指定onResolved()回调和onRejected()回调,而finall()只需要写一次。

其实现原理如下:

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 })
    );
};

all()

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

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

如果p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调;

如果p1、p2、p3中有一个状态为rejected,p的状态就会变成rejected,此时第一个被reject的实例的返回值,传递给p的回调;

  • 如果我们想将成功的value和错误的reason都放到一个数组中,可以像下面这样做:

    const p1 = new Promise((resolve, reject) => {
        resolve('hello');
    })
    .then(result => result)
    .catch(e => e); // 添加了该语句
    // 事实上p2指向的是catch()返回的Promise实例,该实例指向玩catch()方法后,
    // 也会变成resolved,因此会调用all().then()指定的回调函数。
    // 如果p2没有指定catch()方法,则会调用all().catch()指定的回调函数。
    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: 报错了
          ...
    ]
    

race()

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

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

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变Promise实例的返回值,就传递给p的回调函数。

具体可以这样使用:如果5秒内没有获得结果,就将Promise的状态变为reject:

const p = Promise.race([
    fetch('/resource-that-may-take-a-while'),
    new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error('request timeout')), 5000)
    })
]);

p
    .then(console.log)
    .catch(console.error);

allSettled()

allSettled()将多个Promise实例,包装成一个新的Promise实例。该方法由ES2020引入。

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

只有等到p1、p2、p3都返回结果(不管是什么状态),p才会结束,且状态总是fulfilled。

any()

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

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

只要p1、p2、p3中有一个变成fulfilled状态,p就会变成fulfilled状态;如果p1、p2、p3都变成rejected状态,就会抛出错误(是每个Promise实例的错误组成的数组)。

any()与race()的区别是,any()不会因为某一个Promise变成rejected状态而结束。

resolve()

resolve()用于将现有对象转为Promise对象,状态为fulfilled。

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

根据传入值类型(包括Promise实例、thenable对象、有值但不是前两者、无值)的不同,会有不同的行为。详情见参考资料1。

reject()

resolve()用于将现有对象转为Promise对象,状态为rejected。

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

参考资料

[1] Promise对象 --- 阮一峰

posted @ 2021-05-03 16:10  hdxg  阅读(91)  评论(0)    收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css