async与await

async

功能

async 用于声明异步函数。这个关键字可以用在函数声明、函数表达式、箭头函数和方法上:

async function foo() {}
let bar = async function() {};
let baz = async () => {};
class Qux {
  async qux() {}
}

返回值

async函数返回的是一个Promise对象,可以使用then方法添加回调函数。

1.使用return关键字返回的值,会被Promise.resolve()包装成一个Promise对象。

async function foo() {
    console.log(1);
    return 3;
}
// 给返回的期约添加一个解决处理程序
foo().then(value => console.log(value));
console.log(2);
// 1 2 3

2.返回一个期约对象

async function foo() {
    console.log(1);
    return Promise.resolve(3);
}
// 给返回的期约添加一个解决处理程序
foo().then(value => console.log(value));
console.log(2);
// 1 2 3

3.在async函数中抛出错误会返回拒绝的期约(导致Promise对象变为reject状态)

async function foo() {
    console.log(1);
    throw 3;
}
// 给返回的期约添加一个拒绝处理程序
foo().catch(reason => console.log(reason));
console.log(2);
// 1 2 3

4.注意 拒绝期约的错误不会被异步函数捕获

async function foo() {
 console.log(1);
 Promise.reject(3);
}
// 给返回的期约添加一个拒绝处理程序
foo().catch(reason => console.log(reason));
console.log(2);
// 1 2
// Uncaught (in promise): 3

await

一般情况下,await命令后面是一个Promise对象;如果不是,会被转成一个立即resolve的Promise对象(会被当作已经解决的期约)。
异步函数主要针对不会马上完成的任务,所以需要一种暂停和恢复执行的能力。使用 await关键字可以暂停异步函数代码的执行,等待期约解决。

let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3)); 
p.then((x) => console.log(x)); // 3 

使用 async/await 可以写成这样:

async function foo() { 
 let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3)); 
 console.log(await p); 
} 
foo(); // 等待一秒输出3

注意,await 关键字会暂停执行异步函数后面的代码,让出 JavaScript 运行时的执行线程。
await 关键字同样是尝试解包对象的值,然后将这个值传给表达式,再异步恢复异步函数的执行。

await等待的值

等待一个原始值

async function foo() { 
 console.log(await 'foo'); 
} 
foo(); 
// foo

等待一个期约

async function qux() { 
 console.log(await Promise.resolve('qux')); 
} 
qux(); 
// qux

等待会抛出错误的同步操作,会返回拒绝的期约

async function foo() { 
 console.log(1); 
 await (() => { throw 3; })(); 
} 
// 给返回的期约添加一个拒绝处理程序
foo().catch(console.log);
console.log(2); 
// 1 
// 2 
// 3 

如前面的例子所示,单独的 Promise.reject()不会被异步函数捕获,而会抛出未捕获错误。

对拒绝的期约使用 await 则会释放(unwrap)错误值(将拒绝期约返回)

async function foo() { 
 console.log(1); 
 await Promise.reject(3); 
 console.log(4); // 这行代码不会执行
} 
// 给返回的期约添加一个拒绝处理程序
foo().catch(console.log); 
console.log(2); 
// 1 
// 2 
// 3

理解执行过程

函数运行时在碰到 await 关键字时,会记录在哪里暂停执行。等到 await 右边的值可用了,JavaScript 运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。
(当函数执行时,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。)
因此,即使 await 后面跟着一个立即可用的值,函数的其余部分也会被异步求值。 如下代码:

async function foo() { 
 console.log(2); 
 await null; 
 console.log(4); 
} 
console.log(1); 
foo(); 
console.log(3);
//1 2 3 4

(1) 打印 1;
(2) 调用异步函数 foo();
(3)(在 foo()中)打印 2;
(4)(在 foo()中)await 关键字暂停执行,为立即可用的值 null 向消息队列中添加一个任务;
(5) foo()退出;
(6) 打印 3;
(7) 同步线程的代码执行完毕;
(8) JavaScript 运行时从消息队列中取出任务,恢复异步函数执行;
(9)(在 foo()中)恢复执行,await 取得 null 值(这里并没有使用);
(10)(在 foo()中)打印 4;
(11) foo()返回。

async function foo() { 
  console.log(2); 
  console.log(await Promise.resolve(8)); 
  console.log(9); 
} 
async function bar() {
  console.log(4); 
  console.log(await 6); 
  console.log(7); 
} 
console.log(1); 
foo(); 
console.log(3); 
bar(); 
console.log(5);
// 1 2 3 4 5 8 9 6 7

(1) 打印 1;
(2) 调用异步函数 foo();
(3)(在 foo()中)打印 2;
(4)(在 foo()中)await 关键字暂停执行,向消息队列中添加一个期约在落定之后执行的任务;
(5) 期约立即落定,把给 await 提供值的任务添加到消息队列;
(6) foo()退出;
(7) 打印 3;
(8) 调用异步函数 bar();
(9)(在 bar()中)打印 4;
(10)(在 bar()中)await 关键字暂停执行,为立即可用的值 6 向消息队列中添加一个任务;
(11) bar()退出;
(12) 打印 5;
(13) 顶级线程执行完毕;
(14) JavaScript 运行时从消息队列中取出解决 await 期约的处理程序,并将解决的值 8 提供给它;
(15) JavaScript 运行时向消息队列中添加一个恢复执行 foo()函数的任务;
(16) JavaScript 运行时从消息队列中取出恢复执行 bar()的任务及值 6;
(17)(在 bar()中)恢复执行,await 取得值 6;
(18)(在 bar()中)打印 6;
(19)(在 bar()中)打印 7;
(20) bar()返回;
(21) 异步任务完成,JavaScript 从消息队列中取出恢复执行 foo()的任务及值 8;
(22)(在 foo()中)打印 8;
(23)(在 foo()中)打印 9;
(24) foo()返回。

async function foo() { 
 console.log(await Promise.resolve('foo')); 
} 
async function bar() { 
 console.log(await 'bar'); 
} 
async function baz() { 
 console.log('baz'); 
} 
foo(); 
bar(); 
baz();
//baz foo bar

await命令

  • 正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
async function f() {
  // 等同于
  // return 123;
  return await 123;
}
f().then(v => console.log(v))
// 123
  • 另一种情况是,await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于 Promise 对象。
  • await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。
async function f() {
  await Promise.reject('出错了');
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了

注意,上面代码中,await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数。这里如果在await前面加上return,效果是一样的。

  • 任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。

  • 第一种方法:将第一个await放在try…catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。
async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// hello world
  • 第二种方法:await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。
 async function f() {
  await Promise.reject('出错了')
    .catch(e => console.log(e));
  return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// 出错了
// hello world
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值