解决回调地域的promise详解+async/await

一、Promise 对象

描述:Promise一个异步编程的构造函数,比传统的回调函数更加强大,避免了层层嵌套的回调函数。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

特点:

  1.  状态不受外界影响。 三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态只能是pending=>fulfilled or pending=>rejected
  3. Promise对象提供统一的接口,使得控制异步操作更加容易。

基本用法:

1 function timeout(ms) {
2   return new Promise((resolve, reject) => {
3     setTimeout(resolve, ms, 'done');
4   });
5 }
6 
7 timeout(100).then((value) => {
8   console.log(value);
9 });

调用resolvereject时有参数,参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数参数除了正常的值,还可能是另一个 Promise 实例,比如像下面这样。

const p1 = new Promise(function (resolve, reject) {
  // ...
});

const p2 = new Promise(function (resolve, reject) {
  // ...
  resolve(p1);
})
上面代码中,一个异步操作的结果是返回另一个异步操作。
注意,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
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

上面代码中,p1是一个 Promise,3 秒之后变为rejectedp2的状态在 1 秒之后改变,resolve方法返回的是p1
由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。
所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

调用resolvereject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolvereject的后面。所以,推荐在它们前面加上return语句,这样就不会有意外。

1 new Promise((resolve, reject) => {
2   return resolve(1);
3   // 后面的语句不会执行
4   console.log(2);
5 })

 

Promise原型上的then方法和catch方法

作用:为 Promise 实例添加状态改变时的回调函数。两个参数值都是可选回调函数,第一个成功回调,参数resolved的值,第二个错误回调,参数为错误的reason。

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

const someAsyncThing = function () {
  return new Promise(function (resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing().then(function () {
  console.log('everything is great');
}).catch(res => {
  console.log(res + 123);
});

setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123

上面代码 由于捕获了错误,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程、终止脚本执行。

Promise 内部的错误不会影响到 Promise 外部的代码,即使回调函数里面有错误,通俗的说法就是“Promise 会吃掉错误”。

为此在nodejs中有个unhandleRejection事件专门监听promsie未捕获的reject错误。

process.on('unhandledRejection', function (err, p) {
  throw err;
});
上面代码中,unhandledRejection事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。

1 const promise = new Promise(function (resolve, reject) {
2   resolve('ok');
3   setTimeout(function () { throw new Error('test') }, 0)
4 });
5 promise.then(function (value) {});
6 // ok
7 // Uncaught Error: test

 

 

 

上面代码中,Promise 指定在下一轮“事件循环”再抛出错误。到了定时器执行时候,promise已经执行结束,所以这个错误实在函数体外抛出的,会冒泡到外层???成了未捕获的错误

 

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing()
.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on

 

 

 

 

Promise原型上的finally方法

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

Promise.all() 

将多个Promise的实例包装为一个新的Promise实例,接受promise数组。如:

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

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

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

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

Promise.race() 

race英文意思比赛,语法和all一样。拿到最新改变状态那个promise的返回值。

 

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

  上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。

 

Promise.any()

和race差不多,但是他是所有都变为reject才改变为reject,resolve的话拿到最先成功那个值

Promise.any([
  fetch('https://v8.dev/').then(() => 'home'),
  fetch('https://v8.dev/blog').then(() => 'blog'),
  fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => {  // 只要有一个 fetch() 请求成功
  console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
  console.log(error);
});

  

Promise.resolve() 

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


二、async函数

描述:Generator函数的语法糖。一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

async函数对 Generator 函数的改进如下:

  1. 内置执行器。Generator 函数的执行必须靠执行器,所以才有了co模块(co模块需要去学习一下),而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
  2. 更好的语义。async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值会自动转成立即 resolved 的 Promise 对象)。
  3. 更广的适用性。
  4. 返回值是 Promise。Generator 函数的返回值是 Iterator,你可以用then方法指定下一步的操作。

返回 Promise 对象

async函数返回一个 Promise 对象。

async函数内部return语句返回的值,会成为then方法回调函数的参数。

async function f() {
  return 'hello world';
}

f().then(v => console.log(v))
// "hello world"

 








 

posted @ 2023-03-21 21:32  游向戈壁滩的鱼  阅读(16)  评论(0)    收藏  举报