当涉及到 JavaScript 中的异步编程时,async/await
是一个强大而直观的工具,它使得处理异步代码更加容易理解和编写。让我们一步步地深入了解它。
1. 异步编程背景
在 JavaScript 中,某些操作可能需要一些时间来完成,比如网络请求、文件读取、定时器等。为了不阻塞程序的执行,JavaScript 使用了异步编程模型,即不等待某些操作完成,而是继续执行下面的代码。
2. Promise
在ES6中引入了 Promise
对象,用于更好地处理异步操作。但是,使用 Promise
仍然需要嵌套回调函数,导致代码可读性不佳,这就是所谓的"回调地狱"问题。
有关Promise的内容,可以看我对于promise的理解以及promise解决了什么问题这两个文章
3. async/await 的引入
为了解决回调地狱问题,ES2017(ES8)引入了 async/await
。这是一种在写异步代码时更具可读性和可维护性的语法糖。
什么是语法糖
"语法糖"是计算机科学中的一个概念,指的是一种在语言中添加的、对语言的语法结构进行改进,使得特定的代码模式更容易编写或更易于阅读的语法形式。语法糖并不引入新的功能,而是提供了一种更便捷、更易读的语法。
这个术语的意义在于,尽管它使得代码编写更加方便,但它本质上并没有改变语言的功能或能力。它只是一种语法层面上的改进,简化了代码的书写方式,使得开发者更容易理解和使用某些特性。
一个常见的例子是前文提到的
async/await
,它被认为是 JavaScript 中的一种语法糖。使用async/await
让异步代码看起来更像同步代码,但它本质上仍然是基于Promise
的异步编程。另一个例子是 Python 中的列表推导式。列表推导式提供了一种简洁的语法来创建列表,但实际上它是对常规循环的一种语法糖。
总的来说,语法糖是一种为了提高代码可读性和编写效率而引入的语法结构。
4. async 函数
async
关键字用于定义一个返回 Promise
的异步函数。使用 async
声明的函数内部,可以使用 await
关键字来暂停函数执行,等待 Promise
解决,语法上强制规定 await 只能出现在 asnyc 函数中
javascript
async function fetchData() {
// 异步操作,比如请求数据
let result = await fetchDataFromServer();
console.log(result);
}
5. await 表达式
await
只能 在 async
函数内部使用,用于等待一个表达式解决为 Promise
。当 await
后面的表达式被解决时,它返回解决的值。
javascript
async function fetchData() {
let result = await fetchDataFromServer();
console.log(result);
}
// fetchDataFromServer 返回一个 Promise
function fetchDataFromServer() {
return new Promise(resolve => {
setTimeout(() => {
resolve("Data fetched successfully!");
}, 2000);
});
}
// 使用
fetchData();
6. 错误处理
使用 try/catch
来处理异步函数中的错误,这比传统的回调错误处理更为直观。
javascript
async function fetchData() {
try {
let result = await fetchDataFromServer();
console.log(result);
} catch (error) {
console.error("Error fetching data:", error);
}
}
7. 同步化的写法
async/await
使得异步代码的写法更类似于同步代码,提高了代码的可读性和可维护性。
javascript
async function fetchDataSequentially() {
let result1 = await fetchDataFromServer1();
console.log(result1);
let result2 = await fetchDataFromServer2();
console.log(result2);
// ...
}
8.一些有关概念
-
返回 Promise 对象:
async
函数总是返回一个Promise
对象。这是因为async
函数内部使用Promise.resolve()
来封装函数返回的值。无论async
函数返回的是一个直接量还是一个经过Promise.resolve()
包装的值,最终都会被封装成一个Promise
对象。javascriptasync function exampleAsyncFunction() { return 42; } const resultPromise = exampleAsyncFunction(); console.log(resultPromise); // Promise { 42 }
-
无返回值情况: 如果
async
函数没有显式的return
语句,或者return
的是一个没有使用await
关键字的表达式,那么该async
函数会隐式返回Promise.resolve(undefined)
。javascriptasync function exampleAsyncFunction() { // 没有 return 或者 return 后没有使用 await } const resultPromise = exampleAsyncFunction(); console.log(resultPromise); // Promise { undefined }
-
无等待立即执行: 在没有
await
的情况下执行async
函数时,它会立即执行并返回一个Promise
对象。这是因为async
函数内部的代码会立即执行,不会等待异步操作完成。javascriptasync function exampleAsyncFunction() { console.log("Async function is executing"); return 42; } const resultPromise = exampleAsyncFunction(); console.log(resultPromise); // Promise { 42 }
-
Promise.resolve(x)
的简写:Promise.resolve(x)
是一个用于快速将值x
封装成Promise
对象的方法。它是Promise
构造函数的一个静态方法。你可以把它看作是new Promise(resolve => resolve(x))
的简写形式,用于将字面量对象或其他对象封装成Promise
实例。javascriptconst value = 42; const promise = Promise.resolve(value); console.log(promise); // Promise { 42 }
总结
async/await
是 JavaScript 异步编程的一个巨大进步,使得异步代码更易于编写和理解。通过使异步代码看起来像同步代码,它提高了可读性,同时保留了异步编程的非阻塞特性。