JavaScript 如何链式调用异步函数
JavaScript是单线程的异步编程语言。因此,一些耗时的操作,如I/O、数据库访问、网络调用等,会异步执行,以避免中断JS线程中的其他事务。这可以通过异步代码(如promise或async函数)来实现。异步函数很酷,但它们的执行时间是不确定的,这有时会在存在相互依赖的两个异步函数时创建问题。
注意: 本文将使用ES2015+的特性,如async函数、箭头函数等。然而,同样的结果也可以使用较旧的ES特性来实现。
示例1: 为了帮助我们解决这个问题,我们有三个异步函数(黑盒函数)。
Javascript
<script>
async function getUserData() {
console.log('Fetching data');
}
async function cleanUserData(userData) {
console.log('Cleaning data');
}
async function saveToDataBase(userData) {
console.log('Saving to DB');
}
const userData = getUserData();
const cleanedData = cleanUserData(userData);
saveToDataBase(cleanedData);
</script>
输出: 这些是异步函数,它们的执行时间并不总是相同。因此,这些函数可以以任何可能的顺序运行。下面是一个可能的输出。
Cleaning data
Saving to DB
Fetching data
有两种方法可以运行这些函数并强制函数执行的顺序。
1. 使用 Promise 的 .then()
Javascript
<script>
async function getUserData() {
console.log('Fetching data');
}
async function cleanUserData(userData) {
console.log('Cleaning data');
}
async function saveToDataBase(userData) {
console.log('Saving to DB');
}
getUserData()
.then((userData) => cleanUserData(userData))
.then((cleanedData) => saveToDataBase(cleanedData))
.then(() => console.log('All work done'))
</script>
输出:
Fetching data
Cleaning data
Saving to DB
All work done
这很好并且起作用,但有时会导致回调地狱,可能在看到代码时影响你的心情(开个玩笑)。
**2. 使用 **async/ await :
有一种更清晰的方法来完成同样的事情,使用 await 关键字等待函数执行完成。 await 只能在 async 函数内部工作。所以,我们需要将其包裹在一个包装函数中。
Javascript
<script>
async function getUserData() {
console.log('Fetching data');
}
async function cleanUserData(userData) {
console.log('Cleaning data');
}
async function saveToDataBase(userData) {
console.log('Saving to DB');
}
async function cleanAndSaveUserData() {
const userData = await getUserData();
const cleanedData = await cleanUserData(userData);
await saveToDataBase(cleanedData);
console.log('All work done');
}
cleanAndSaveUserData(); // does all the work
</script>
输出:
Fetching data
Cleaning data
Saving to DB
All work done
注意: 为简单起见,本文不涵盖链式async()函数的错误处理。