async/await 以及同步、异步任务的执行过程

文章目录

  • 语法
  • 参数
  • 返回值
  • 描述
    • async
    • await
  • 同步任务和异步任务的执行过程
  • 测试样例一
  • 测试样例二

async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
在其出现之前,开发者只能通过 链式.then() 的方式处理promise异步操作,这样写出的代码冗余且不易读

语法

async function name([param[, param[, ... param]]]) {
    statements 
}

参数

name
函数名称

param
要传递给函数的参数的名称

statements
包含函数主题的表达式,可以await机制,且await必须包含在async之下

返回值

一个Promise,这个promise要么会通过一个由async函数返回的值被解决,要么会通过一个从async函数中抛出的(或其中没有被捕获到的)异常被拒绝。

描述

async

async函数可能包含0个或者多个await表达式。await表达式会暂停整个async函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程。promise的解决值会被当作该await表达式的返回值。

在async中第一个await之前的代码会被同步执行,遇到await之后开始异步操作,就会退出当前async函数的执行进而执行函数下面的操作,直到那个await异步操作完成,会继续async函数里下面的同步操作,若再遇到await又会开启一个异步操作,并且退出当前async函数继续其下面的步骤,一直等到这个await操作完成。

async函数一定会返回一个promise对象。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。

async function foo() {
   return 1
}

//等价于
function foo() {
   return Promise.resolve(1)
}

使用async / await关键字就可以在异步代码中使用普通的try / catch代码块。

注意:promise链不是一次就构建好的,相反,promise链是分阶段构造的,因此在处理异步函数时必须注意对错误函数的处理。

例如,在下面的代码中,在promise链上配置了.catch处理程序,将抛出未处理的promise错误。这是因为p2返回的结果不会被await处理。

async function foo() {
   const p1 = new Promise((resolve) => setTimeout(() => resolve('1'), 1000))
   const p2 = new Promise((_,reject) => setTimeout(() => reject('2'), 500))
   const results = [await p1, await p2] // 不推荐使用这种方式,请使用 Promise.all或者Promise.allSettled 
   //await Promise.all([p1, p2])
}
foo().catch(() => {}) // 捕捉所有的错误...

async函数的函数体可以被看作是由0个或者多个await表达式分割开来的。从第一行代码直到(并包括)第一个await表达式(如果有的话)都是同步运行的。
这样的话,一个不含await表达式的async函数是会同步运行的。然而,如果函数体内有一个await表达式,async函数就一定会异步执行。

async function foo() {
   await 1
}
//等价于
function foo() {
   return Promise.resolve(1).then(() => undefined)
}

await

await 表达式会暂停当前 async function 的执行(),等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。

在async中第一个await之前的代码会被同步执行,遇到await之后开始异步操作,就会退出当前async函数的执行进而执行函数下面的操作,直到那个await异步操作完成,会继续async函数里下面的同步操作,若再遇到await又会开启一个异步操作,并且退出当前async函数继续其下面的步骤,一直等到这个await操作完成。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。

另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}
f1();



同步任务和异步任务的执行过程

async/await 以及同步、异步任务的执行过程_第1张图片

  1. 同步任务有JavaScript主线程次序执行
  2. 异步任务委托给宿主环境执行
  3. 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
  4. JavaScript主线程的执行栈被清空后,会读取任务队列中的回调函数,按次序执行

JavaScript主线程不断重复上面的过程

测试样例一

import thenFs from 'then-fs'

console.log('A')
async function getAllFile() {
  console.log('B')
  const r1 = await thenFs.readFile('./files/1.txt', 'utf8')
  const r2 = await thenFs.readFile('./files/2.txt', 'utf8')
  const r3 = await thenFs.readFile('./files/3.txt', 'utf8')

  console.log(r1,r2,r3)
  console.log('D')
}

getAllFile()
console.log('C')

运行结果

A
B
C
111 222 333
D

测试样例二

var slowPromise = function() {
    console.log("starting slow promise");
    return new Promise(resolve => {
      setTimeout(function() {
        resolve("slow");
        console.log("slow promise is done");
      }, 2000);
    });
  };
  
  var fastPromise = function() {
    console.log("starting fast promise");
    return new Promise(resolve => {
      setTimeout(function() {
        resolve("fast");
        console.log("fast promise is done");
      }, 1000);
    });
  };
  
  var sequentialStart = async function() {
    console.log('=========SEQUENTIAL START===========');
  
    // 1. Execution gets here almost instantly
    const slow = await slowPromise();
    console.log(slow); // 2. this runs 2 seconds after 1.
  
    const fast = await fastPromise();
    console.log(fast); // 3. this runs 3 seconds after 1.
  }
  
  var concurrentStart = async function() {
    console.log('===========CONCURRENT START with await===========');
    const slow = slowPromise(); // starts timer immediately
    const fast = fastPromise(); // starts timer immediately
  
    // 1. Execution gets here almost instantly
    console.log(await slow); // 2. this runs 2 seconds after 1.
    console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
  }
  
  var concurrentPromise = function() {
    console.log('===========CONCURRENT START with Promise.all===========');
    return Promise.all([slowPromise(), fastPromise()]).then((messages) => {
      console.log(messages[0]); // slow
      console.log(messages[1]); // fast
    });
  }
  
  var parallel = async function() {
    console.log('===========PARALLEL with await Promise.all===========');
  
    // Start 2 "jobs" in parallel and wait for both of them to complete
    await Promise.all([
        (async()=>console.log(await slowPromise()))(),
        (async()=>console.log(await fastPromise()))()
    ]);
  }
  
  // This function does not handle errors. See warning below!
  var parallelPromise = function() {
    console.log('===========PARALLEL with Promise.then===========');
    slowPromise().then((message)=>console.log(message));
    fastPromise().then((message)=>console.log(message));
  }
  
  sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"
  
  // wait above to finish
  setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast"
  
  // wait again
  setTimeout(concurrentPromise, 7000); // same as concurrentStart
  
  // wait again
  setTimeout(parallel, 10000); // truly parallel: after 1 second, logs "fast", then after 1 more second, "slow"
  
  // wait again
  setTimeout(parallelPromise, 13000); // same as parallel
  

运行结果

=========SEQUENTIAL START===========
starting slow promise
slow promise is done
slow
starting fast promise
fast promise is done
fast
===========CONCURRENT START with await===========
starting slow promise
starting fast promise
fast promise is done
slow promise is done
slow
fast
===========CONCURRENT START with Promise.all===========
starting slow promise
starting fast promise
fast promise is done
slow promise is done
slow
fast
===========PARALLEL with await Promise.all===========
starting slow promise
starting fast promise
fast promise is done
fast
slow promise is done
slow
===========PARALLEL with Promise.then===========
starting slow promise
starting fast promise
fast promise is done
fast
slow promise is done
slow

个人感觉这块很不容易理解,建议多测试一下样例,体会一下其中精妙~

你可能感兴趣的