async/await 连体婴

前言

非同步概念的最後一小块拼图,要来学习怎麽使用 async/await
async/await 是 ECMAScript 2017 引入的语法糖,对於遇到非同步脑袋就打结的我,async/await 简直就是救星~
async/await 像个连体婴一样总是形影不离,不过它们长得还是不一样嘛,所以还是先分开认识一下

async

如果在一般函式前加上 async 关键字,代表我们宣告了一个非同步函式
先来看看一般函式,和在同一个函式前加上 async 的区别

可以观察到只要在函式前加上 async,就保证这个函式会回传一个 Promise,即使函式的回传值只是单纯的字串或数字,也会自动用 Promise 包裹起来

await

await 会放在非同步函式前,他的作用是等待非同步函式执行完成并回传结果,不需要像 Promise 得使用.then() 取得 resolve 的值。

//非同步函式,等待两秒後回传顾客点的饮料
function makeDrinks(drinks) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(drinks);
    }, 2000);
  });
}


async function sellDrinks() {
  const firstOrder = await makeDrinks("black tea");
  console.log(firstOrder);
}

sellDrinks(); // black tea
  • 要注意的是 await 必须放在 async 函式中,如果放在一般函式会报错!
// 在一般函式内使用 await 会报错
function sellDrinks() {
  const firstOrder = await makeDrinks("black tea");
  console.log(firstOrder);
}

sellDrinks();

//SyntaxError: await is only valid in async function

async/await 执行顺序

对关键字有基础认识後,用一个简单的范例理解 async/await 的执行步骤

async function foo() {
   const result = await new Promise((resolve) => setTimeout(() => resolve('1'), 1000))
   console.log(result)
}
foo();

// 一秒过後,印出1
  1. 执行 foo 函式,函式进到第一行後,await 接的非同步函式带有一个 pending 的 Promise,程序码不会继续往下执行,控制权回到呼叫 foo 的函式
  2. 一秒过後,Promise 的状态由 pending 转为 fulfilled,控制权重新回到 foo 函式中,resolve 的值这时就会赋值给 result
  3. 程序码继续往下走,印出结果
  4. 控制权会转移到 return 表达式 ,如果没有return,则回传预设的undefined

不同写法的执行时间差异

理解执行的顺序後,应该就不难理解下述的例子为什麽执行时间会是4秒

function makeDrinks(drinks) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(drinks);
    }, 2000);
  });
}

async function sellDrinks() {
  console.time();
  const firstOrder = await makeDrinks("black tea");
  const secondOrder = await makeDrinks("bubble tea");
  console.log(`Here is your ${firstOrder}`);
  console.log(`Here is your ${secondOrder}`);
  console.timeEnd();
}


// Here is your black tea
// Here is your bubble tea
// default: 4.023s

下面两个例子的执行时间大约都是两秒左右,但 MDN 并不建议第一种做法,如果希望非同步函式同时执行的话,建议使用Promise.all()Promise.allSettled()

// 写法1
async function sellDrinks() {
  console.time();
  const order = makeDrinks("black tea");
  const extraOrder = makeDrinks("bubble tea");
  const totalOrder = (await order) + "," + (await extraOrder);
  console.log(`Here is your ${totalOrder}`);
  console.timeEnd();
}

sellDrinks();
// Here is your black tea,bubble tea
// default: 2.029s


//写法2
async function sellDrinks() {
  console.time();
  const totalOrder = await Promise.all([
    makeDrinks("black tea"),
    makeDrinks("bubble tea"),
  ]);
  console.log(`Here is your ${totalOrder[0]}`);
  console.log(`Here is your ${totalOrder[1]}`);
  console.timeEnd();
}

sellDrinks();

// Here is your black tea
// Here is your bubble tea
// default: 2.031s

小结

使用了 async/await 後,不仅写非同步感觉像在写同步,在错误处理方面也可以用熟悉的try...catch语法。

之前看着学姊用 async/await 觉得好羡慕啊,写起来轻松又好读,但自己要写却不知从何写起,现在回头想想大概是因为当时尚未理解非同步的整个脉络。
慢慢从 callback 循序理解 Promise 的使用,再尝试将 Promise 替换成 async/await 後,对於 Promise 又再理解了一些些。/images/emoticon/emoticon42.gif

参考资料:
MDN
JAVASCRIPT.INFO
Asynchronous 非同步进化顺序 - Async/Await


<<:  Python GUI - 要如何在画面上显示右键选单呢?

>>:  Day 22 | 状态管理套件 MobX - 基本使用

DAY 11- 区块操作模式

"什麽叫你只会加密128位元?" --- 花了不少篇幅介绍两中区块加密方式,DE...

[Day27] 监视股价 - Watcher

找到股价站上 20 周线只是第一步,不是一站上就会开始飙升,我还会搭配价位突破「箱型区间」,这个突破...

[Day30] 总结

终於到最後一天啦! 在嘉实工作 5 年多来,虽然一直知道公司发展的 XS 是国内程序交易的先驱者,...

Day15 Sideproject(作品集) from 0 to 1 -刻画面

昨天把整个专案架起来了 今天我们就可以开始来刻画面了 这边也要提一下之前我们其实是直接开写 先从会员...

DAY21:AsyncTask类别之实作

今天的实作要透过一个计算BMI的小程序来使用AsyncTask类别,在这之中,我们要加入Thread...