asyncawait本质上是生成器函数yield的语法糖。
在这之前如果程序中的其他代码要访问Promise的值,则需要写一个解决处理程序:

  1. let p = new Promise((resolve, reject) => {
  2. setTimeout(()=>{
  3. resolve(3);
  4. }, 1000)
  5. });
  6. p.then((res) => console.log(res)); // 3

这其实是很不方便的,因为其他代码都必须塞到then处理程序中。为此,ECMAScript对函数进行了扩展,为其增加了两个新关键字:asyncawait

async

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

  1. async function foo() {}
  2. let bar = async function() {};
  3. let baz = async () => {};
  4. class Qux {
  5. async qux() {}
  6. }

使用async关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值的。

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

如果异步函数内return值,这个值会被Promise.resolve()包装成一个Promise对象。异步函数始终返回Promise对象。

  1. async function read() {
  2. return "abc";
  3. }
  4. console.log(read()); // Promise {<fulfilled>: 'abc'}
  5. read().then((res) => {
  6. console.log(res); // abc
  7. });

如果异步函数内发生错误,会直接将Promise的状态更改为失败:

  1. async function read() {
  2. console.log(a);
  3. return "abc";
  4. }
  5. read().catch((err) => {
  6. console.log(err); // ReferenceError: a is not defined
  7. });

当异步函数内返回失败状态后,下面的代码将不会继续执行:

  1. async function read() {
  2. let res = await Promise.reject("错误");
  3. console.log(123); // 不执行
  4. return res;
  5. }
  6. read().catch((err) => {
  7. console.log(err); // 错误
  8. });

await

因为异步函数主要针对不会马上完成的任务,所以自然需要一种暂停和恢复执行的能力。使用**await**关键字可以暂停异步函数代码的执行,等待解决。

  1. let p = new Promise((resolve, reject) => {
  2. setTimeout(()=>{
  3. resolve(3)
  4. }, 1000)
  5. });
  6. p.then((res) => console.log(res)); // 3
  7. // 使用 async/await 可以写成这样:
  8. async function foo() {
  9. let p = new Promise((resolve, reject) => {
  10. setTimeout(()=>{
  11. resolve(3)
  12. }, 1000)
  13. });
  14. console.log(await p);
  15. }
  16. foo(); // 3

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

  1. // 异步打印 "foo"
  2. async function foo() {
  3. console.log(await Promise.resolve('foo'));
  4. }
  5. foo(); // foo
  6. // 异步打印 "bar"
  7. async function bar() {
  8. return await Promise.resolve('bar');
  9. }
  10. bar().then(console.log); // bar
  11. // 1000 毫秒后异步打印 "baz"
  12. async function baz() {
  13. await new Promise((resolve, reject) => {
  14. setTimeout(()=>{
  15. resolve()
  16. }, 1000)
  17. });
  18. console.log('baz');
  19. }
  20. baz(); // baz(1000 毫秒后)

**JavaScript**运行时在碰到**await**关键字时,会记录在哪里暂停执行。等到**await**右边的值可用了,**JavaScript**运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。

  1. function request() {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve(2);
  5. }, 3000);
  6. });
  7. }
  8. async function getData() {
  9. let res = await request();
  10. console.log(3);
  11. return res;
  12. }
  13. console.log(1);
  14. getData().then((res) => {
  15. console.log(res);
  16. });
  17. console.log(4);
  18. // 1
  19. // 4
  20. // 3
  21. // 2

await关键字必须在异步函数中使用,不能在顶级上下文如<script>标签或模块中使用。不过, 定义并立即调用异步函数是没问题的。

  1. async function foo() {
  2. console.log(await Promise.resolve(3));
  3. }
  4. foo(); // 3
  5. // 立即调用的异步函数表达式
  6. (
  7. async function() {
  8. console.log(await Promise.resolve(3));
  9. }
  10. )();
  11. // 3

await能够被try...catch...进行捕获:

  1. async function promise() {
  2. function p1() {
  3. return new Promise((resolve) => {
  4. resolve(1);
  5. });
  6. }
  7. function p2() {
  8. return new Promise((resolve, reject) => {
  9. reject(2);
  10. });
  11. }
  12. function p3() {
  13. return new Promise((resolve, reject) => {
  14. resolve(3);
  15. });
  16. }
  17. try {
  18. var p11 = await p1();
  19. var p22 = await p2();
  20. var p33 = await p3();
  21. console.log(p11);
  22. console.log(p22);
  23. console.log(p33);
  24. } catch (e) {
  25. console.log("发生错误:" + e); // 发生错误:2
  26. }
  27. }
  28. promise();

错误捕获

:::info 1、try...catch...能捕获await的异常
2、promise的异常只能通过.catch捕获
3、setTimeout的异常只能在回调函数内进行捕获 :::

1、无法捕获new Promise里面的错误

  1. function foo() {
  2. try {
  3. return new Promise((resolve, reject) => {
  4. console.log(a);
  5. });
  6. } catch (error) {
  7. // 无法捕获
  8. console.log("error: " + error);
  9. }
  10. }
  11. foo().catch((err) => console.log(err)); // a is not defined
  1. function foo() {
  2. try {
  3. return new Promise((resolve, reject) => {
  4. reject("abc");
  5. });
  6. } catch (error) {
  7. // 无法捕获
  8. console.log("error: " + error);
  9. }
  10. }
  11. foo().catch((err) => console.log(err)); // abc

2、可以捕获到await

  1. async function foo() {
  2. try {
  3. await new Promise((resolve, reject) => {
  4. reject("abc");
  5. });
  6. } catch (error) {
  7. console.log("error: " + error); // error: abc
  8. }
  9. }
  10. foo();

3、无法捕获setTimeout

  1. function foo() {
  2. try {
  3. setTimeout(() => {
  4. console.log(a); // a is not defined
  5. }, 500);
  6. } catch (error) {
  7. // 无法捕获
  8. console.log("error: " + error);
  9. }
  10. }
  11. foo();

相关链接: