2021.03.04 更新

2021.03.04

MDN好好看看:async函数

串行并行比较

show me the code.

  1. // Promise写法
  2. let p = new Promise((resolve) => {
  3. setTimeout(() => {
  4. resolve("oppos");
  5. }, 2000);
  6. });
  7. p.then((result) => {
  8. console.log(result);
  9. });
  10. // async await写法
  11. function resolveAfter2Seconds() {
  12. return new Promise((resolve) => {
  13. setTimeout(() => {
  14. resolve("oppos");
  15. }, 2000);
  16. });
  17. }
  18. async function asyncCall() {
  19. const result = await resolveAfter2Seconds();
  20. console.log(result);
  21. }

从上面代码看出,在await表达式下面的代码可以被认为是存在于链式调用的then回调中,多个await表达式都将加入链式调用的then回调中,返回值将作为最后一个then回调的返回值。
串行、并行关系也可与Promise写法进行比较,async/await写法更简洁:串并行写法比较.js

async函数一定会返回一个promise对象。

  1. async function foo() {
  2. return 1
  3. }
  4. // 等价于
  5. async function foo() {
  6. return Promise.resolve(1);
  7. }

一个简单的语法比较,并判断输出顺序:

  1. async function sums(a, b) {
  2. console.log("a,b");
  3. console.log(await 22);
  4. // 等价于
  5. new Promise((resolve) => {
  6. resolve(33);
  7. }).then((res) => {
  8. console.log(res);
  9. });
  10. // 等价于
  11. Promise.resolve(55).then((res) => {
  12. console.log(res);
  13. });
  14. return a + b;
  15. }
  16. console.log("object", "start");
  17. sums(5, 6).then((res) => {
  18. console.log(res);
  19. });
  20. console.log("object", "end");

2020.08.26 版本 — 主要摘抄自阮一峰的文档,自己也看不懂…

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。是 Generator 函数的语法糖。

2020.08.26

基本用法

在一个函数前添加async,这个函数就会返回Promsie对象。当该函数执行时,遇到await就会先返回,等到异步完成,再接着执行函数体后面的语句。

async函数返回一个 Promise 对象,内部return语句返回的值,会成为then方法回调函数的参数:

  1. async function f() {
  2. return 'hello world';
  3. }
  4. f().then(v => console.log(v))
  5. // "hello world"

async函数内部抛出错误会被catch方法接收到:

  1. async function f() {
  2. throw new Error('出错了');
  3. }
  4. f().then(
  5. v => console.log(v),
  6. e => console.log(e)
  7. )
  8. // Error: 出错了
  9. // 或这样写
  10. f().catch(e=>console.log(e))

状态变化

async的状态必须等到内部所有await命令后面的Promise对象都执行完才会发生状态改变,除非遇到return或抛出错误。

  1. async function getTitle(url) { // 执行顺序
  2. let response = await fetch(url); // 1
  3. let html = await response.text(); // 2
  4. return html.match(/<title>([\s\S]+)<\/title>/i)[1]; // 3
  5. }
  6. getTitle('https://tc39.github.io/ecma262/').then(console.log)

await命令

await后面的参数:

  1. 若为Promise对象,则返回该对象的结果;
  2. 若为其它,直接返回对应的值;
  3. 若对象包含then方法,将其等同于Promise对象;

await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

错误处理

async函数内部抛出的错误,可以被catch方法捕获到:

  1. async function f() {
  2. await Promise.reject("出错了");
  3. }
  4. f.catch(e=console.log(e));

或者放于try…catch结构里:

  1. async function f() {
  2. try{
  3. await Promise.reject("出错了1");
  4. await Promise.reject("出错了2");
  5. } catch(e){
  6. console.log(e)
  7. }
  8. }

注意:任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

  1. async function f() {
  2. await Promise.reject('出错了');
  3. await Promise.resolve('hello world'); // 不会执行
  4. }

若希望前面的await失败不影响后面的异步操作,有2种方法:
(1)第一个await放在try…catch结构中:

  1. async function f() {
  2. try {
  3. await Promise.reject('出错了');
  4. } catch(e) {console.log(e)}
  5. await Promise.resolve('hello world');
  6. }

(2)第一个await后面的Promise对象跟一个catch方法:

  1. async function f() {
  2. await Promise.reject('出错了')
  3. .catch(e => console.log(e));
  4. await Promise.resolve('hello world');
  5. }

串行与并行

串行异步:

  1. // 只有getFoo完成了getBar才开始执行
  2. let foo = await getFoo();
  3. let bar = await getBar();

并行异步:

  1. // 写法一
  2. let [foo, bar] = await Promise.all([getFoo(), getBar()]);
  3. // 写法二
  4. let fooPromise = getFoo();
  5. let barPromise = getBar();
  6. let foo = await fooPromise;
  7. let bar = await barPromise;
  8. // 同时触发,可以缩短程序执行时间

若两个异步操作互不依赖,则最好使用并行关系来缩短时间。
借助async/await,语义比Promise要简洁、清晰很多。