ES6 Async函数详解:从Generator到Promise的演进与实战

作者:木棉

时间:2026-10-22

浏览:327

本文深入解析ES6 Async函数,阐述其作为Generator函数语法糖的优势,包括内置执行器、语义更清晰、适用性更广及返回Promise对象等特性。文章详细讲解基本用法、语法规则、错误处理机制及并发异步操作的最佳实践,帮助开发者高效掌握现代JavaScript异步编程。

文章目录

本文旨在详细介绍ES6中Async函数的核心内容,供广大开发者参考学习。

Async函数概述

Async函数概述

Async函数的出现极大地简化了异步操作的编写流程,它本质上是Generator函数的语法糖。

为了理解Async函数的优势,我们先看一个使用Generator函数依次读取两个文件的例子:


var fs = require('fs');
var readFile = function (fileName) {
 return new Promise(function (resolve, reject) {
 fs.readFile(fileName, function(error, data) {
  if (error) reject(error);
  resolve(data);
 });
 });
};
var gen = function* () {
 var f1 = yield readFile('/etc/fstab');
 var f2 = yield readFile('/etc/shells');
 console.log(f1.toString());
 console.log(f2.toString());
};

若将其改写为Async函数,代码结构如下:


var asyncReadFile = async function () {
 var f1 = await readFile('/etc/fstab');
 var f2 = await readFile('/etc/shells');
 console.log(f1.toString());
 console.log(f2.toString());
};

相比Generator函数,Async函数在以下四个方面进行了显著改进:

1)内置执行器

Generator函数的执行依赖于外部执行器(如co模块),而Async函数内置了执行器。这意味着Async函数的调用方式与普通同步函数完全一致,仅需一行代码即可触发执行。


var result = asyncReadFile();

例如,调用asyncReadFile函数后,系统会自动执行并输出最终结果。这与需要手动调用next方法或使用co模块才能运行的Generator函数形成鲜明对比。

2)更好的语义表达

async和await关键字比星号(*)和yield具有更清晰的语义。async明确标识函数内包含异步操作,而await则指明其后的表达式必须等待结果返回。

3)更广的适用场景

co模块规定yield命令后只能跟随Thunk函数或Promise对象;而Async函数的await命令后不仅可以是Promise对象,还可以是原始类型的值(如数值、字符串、布尔值),此时等同于同步操作。

4)返回值类型为Promise

Async函数的返回值始终是一个Promise对象,这比Generator函数返回的Iterator对象更为便捷,开发者可以直接使用then方法指定后续操作。

从本质上看,Async函数可视为将多个异步操作封装为一个Promise对象,而await命令则是内部then调用的语法糖。

基本用法

一、基本用法

Async函数返回一个Promise对象,可通过then方法添加回调函数。函数执行时,一旦遇到await关键字便会先返回,待异步操作完成后,再继续执行函数体剩余的语句。


function timeout(ms) {
 return new Promise((resolve) => {
 setTimeout(resolve, ms);
 });
}
async function asyncPrint(value, ms) {
 await timeout(ms);
 console.log(value);
}
asyncPrint('hello world', 5000);

上述代码指定在5000毫秒后输出“hello world”。

由于Async函数返回的是Promise对象,因此它可以作为await命令的参数。基于此特性,上述示例也可改写为以下形式:


async function timeout(ms) {
 await new Promise((resolve) => {
 setTimeout(resolve, ms);
 });
}
async function asyncPrint(value, ms) {
 await timeout(ms);
 console.log(value);
}
asyncPrint('hello world', 5000);

Async函数的多种使用形式


// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
 constructor() {
 this.cachePromise = caches.open('avatars');
 }
 async getAvatar(name) {
 const cache = await this.cachePromise;
 return cache.match(`/avatars/${name}.jpg`);
 }
}
const storage = new Storage();
storage.getAvatar('hzzly').then(…);
// 箭头函数
const foo = async () => {};

语法规范

二、语法规范

Async函数的语法规则总体较为简洁,其难点主要在于错误处理机制。

返回Promise对象

Async函数返回一个Promise对象。函数内部return语句返回的值,将作为then方法回调函数的参数。


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

Promise对象的状态变化

Async函数返回的Promise对象,必须等待内部所有await命令后的Promise对象执行完毕,才会发生状态改变。除非遇到return语句或抛出错误。也就是说,只有当Async函数内部的异步操作全部执行完毕,才会执行then方法指定的回调函数。


async function getTitle(url) {
 let response = await fetch(url);
 let html = await response.text();
 return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"

在上面的代码中,函数getTitle内部包含三个操作:抓取网页、提取文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法中的console.log。

使用注意事项

三、使用注意事项

await命令后的Promise对象运行结果可能是rejected,因此建议将await命令置于try…catch代码块中以捕获异常。


async function myFunction() {
 try {
 await somethingThatReturnsAPromise();
 } catch (err) {
 console.log(err);
 }
}
// 另一种写法
async function myFunction() {
 await somethingThatReturnsAPromise()
 .catch(function (err) {
 console.log(err);
 };
}

对于多个await命令后的异步操作,若它们之间不存在继发关系(即互不依赖),最好让它们同时触发,以提高效率。


//异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。
let foo = await getFoo();
let bar = await getBar();
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

await命令仅能用于Async函数内部,若将其用于普通函数,将会导致报错。

如需更多关于Async函数的使用示例,可参考相关文章:https://www.jb51.net/article/113475.htm

总结

以上就是本文的全部内容,希望这些内容能对各位的学习或工作有所帮助。如有任何疑问,欢迎留言交流,感谢大家对本站的支持。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章