深入理解 JavaScript 中 async 函数与 await 关键字的执行奥秘
前端开发中异步操作的重要性
在如今的前端开发领域,用户对于网页的交互体验要求越来越高。为了实现流畅且高效的交互效果,异步操作成为了前端工程师们不可或缺的 "秘密武器"。想象一下,当你在一个电商网站上浏览商品时,如果每次点击 "加载更多" 按钮都要等待页面完全卡死,直到所有新商品数据都加载完成后才恢复响应,那将会是多么糟糕的体验。这时候,异步操作就能派上用场,它可以让页面在等待数据加载的过程中,依然能够响应用户的其他操作,比如滚动页面、切换分类等。
在 JavaScript 中,我们有多种方式来处理异步操作,像回调函数、Promise 对象等。不过,ES7 引入的async函数和await关键字,为我们处理异步操作带来了更加优雅和便捷的解决方案,瞬间成为了前端工程师们的 "新宠"。接下来,咱们就一起深入探究一下async函数内部的执行流程,以及await关键字在其中扮演的关键角色。
从 JavaScript 的运行机制说起
在深入探讨async函数之前,咱们得先了解一下 JavaScript 的运行机制,因为这是理解async函数执行流程的基础。大家都知道,JavaScript 是一门单线程语言。啥意思呢?简单来说,就是在同一时间,JavaScript 只能执行一个任务。这就好比你吃饭的时候,同一时刻只能用一个勺子往嘴里送食物。
虽然 JavaScript 是单线程的,但在实际应用中,我们经常会遇到一些耗时的操作,比如网络请求获取数据、读取本地大文件等。要是这些操作都以同步的方式执行,那可就麻烦大了。比如,当网页发送一个网络请求去获取用户信息时,如果代码是同步执行的,那么在等待服务器响应的这段时间里,整个页面都会处于卡死状态,用户啥操作都干不了,这显然是我们不愿意看到的。 为了解决这个问题,JavaScript 引入了异步机制。那这个异步机制是怎么实现的呢?其实,JavaScript 把任务分成了两种类型:宏任务(macro - task)和微任务(micro - task)。
宏任务(macro - task)
常见的宏任务有整体代码script(也就是我们写的最外层的 JavaScript 代码块)、setTimeout、setInterval等。当 JavaScript 引擎开始执行代码时,会先把整体代码script作为第一个宏任务放入一个任务队列(也叫任务池)中。每当遇到一个新的宏任务,比如setTimeout设置的定时器回调函数,JavaScript 就会把它也放入这个任务队列中。
然后,JavaScript 引擎会按照任务放入队列的先后顺序,依次从任务队列中取出宏任务来执行。只有当前正在执行的宏任务执行完毕后,才会去任务队列中取下一个宏任务来执行。比如说,我们有这样一段代码:
javascript
// 打印"开始执行script宏任务"
console.log('开始执行script宏任务');
// 设置一个定时器,2秒后执行回调函数
setTimeout(() => {
// 打印"setTimeout回调函数执行"
console.log('setTimeout回调函数执行');
}, 2000);
// 打印"script宏任务执行完毕"
console.log('script宏任务执行完毕');
在这段代码中,首先会执行console.log('开始执行script宏任务');,这是整体代码script这个宏任务中的一部分。接着遇到setTimeout,它会被放入任务队列中,但并不会马上执行。然后继续执行console.log('script宏任务执行完毕');。直到script这个宏任务全部执行完,才会去检查任务队列中是否有新的宏任务。2 秒后,setTimeout的回调函数会被从任务队列中取出并执行,所以最终的打印顺序是:
javascript
开始执行script宏任务
script宏任务执行完毕
setTimeout回调函数执行(2秒后)
微任务(micro - task)
微任务和宏任务有点类似,每个微任务也会被放入一个队列中。不过,微任务有个特殊的地方,它会绑定到当前正在执行的宏任务下面。也就是说,只有当前宏任务下的所有微任务都执行完毕后,这个宏任务才算真正执行结束,JavaScript 引擎才会去执行下一个宏任务。 常见的微任务有Promise对象的then回调函数、MutationObserver API、queueMicrotask()等。咱们来看个例子:
javascript
// 打印"开始执行script宏任务"
console.log('开始执行script宏任务');
// 创建一个Promise对象,立即执行其回调函数
new Promise((resolve) => {
// 打印"Promise内部同步代码执行"
console.log('Promise内部同步代码执行');
// 调用resolve函数,将Promise状态变为resolved
resolve();
})
// 当Promise状态变为resolved时,执行then回调函数,这是一个微任务
.then(() => {
// 打印"Promise then回调函数执行,这是微任务"
console.log('Promise then回调函数执行,这是微任务');
});
// 打印"script宏任务执行完毕"
console.log('script宏任务执行完毕');
在这段代码中,首先执行console.log('开始执行script宏任务');,这是script宏任务的一部分。接着创建Promise对象,Promise内部的同步代码console.log('Promise内部同步代码执行');会立即执行,然后调用resolve函数,将Promise状态变为resolved。此时,then回调函数作为微任务被放入微任务队列中。继续执行console.log('script宏任务执行完毕');,当script宏任务执行完后,会先检查微任务队列。发现有Promise的then回调函数这个微任务,于是执行它,打印出Promise then回调函数执行,这是微任务。所以最终的打印顺序是:
javascript
开始执行script宏任务
Promise内部同步代码执行
script宏任务执行完毕
Promise then回调函数执行,这是微任务
通过了解宏任务和微任务的执行规则,我们对 JavaScript 的异步运行机制有了更清晰的认识。这对于理解async函数内部的执行流程非常关键,因为async函数和await关键字的执行,都和宏任务、微任务有着千丝万缕的联系。
认识 async 函数
async函数是 ES7 引入的一个重要特性,它的出现让我们处理异步操作变得更加简单和直观。简单来说,async关键字用于声明一个异步函数,这个函数会返回一个Promise对象。
声明 async 函数
声明一个async函数非常简单,就像声明一个普通函数一样,只不过在函数定义前面加上async关键字。例如:
javascript
// 声明一个名为fetchData的async函数
async function fetchData() {
// 函数体内部可以写异步操作的代码
}
这里的fetchData就是一个异步函数。当我们调用这个函数时,它会返回一个Promise对象。
async 函数的返回值
说到async函数的返回值,有几种情况需要我们注意。
情况一:返回一个普通值
当async函数返回一个普通值时,这个值会被自动包裹在一个已解决(resolved)状态的Promise对象中返回。比如:
async function getNumber() {
// 返回一个普通数字123
return 123;
}
// 调用getNumber函数,它返回一个Promise对象
getNumber().then((result) => {
// 打印结果123
console.log(result);
});
在这段代码中,getNumber函数返回了一个普通数字123。当我们调用getNumber函数时,它返回的其实是一个状态为resolved,值为123的Promise对象。所以在then回调函数中,我们可以获取到这个值并打印出来。
情况二:返回一个 Promise 对象
如果async函数返回的是一个Promise对象,那么这个Promise对象的状态和值就会直接作为async函数返回的Promise对象的状态和值。例如:
javascript
function createPromise() {
// 返回一个Promise对象,2秒后将状态变为resolved,值为"成功啦"
return new Promise((resolve) => {
setTimeout(() => {
resolve('成功啦');
}, 2000);
});
}
async function getPromise() {
// 返回createPromise函数创建的Promise对象
return createPromise();
}
// 调用getPromise函数,它返回一个Promise对象
getPromise().then((result) => {
// 2秒后打印"成功啦"
console.log(result);
});
在这段代码中,createPromise函数返回一个Promise对象,getPromise这个async函数又返回了createPromise函数创建的Promise对象。所以当我们调用getPromise函数时,它返回的Promise对象的状态和值,就和createPromise函数返回的Promise对象的状态和值一样。2 秒后,Promise状态变为resolved,值为"成功啦",于是在then回调函数中打印出"成功啦"。
情况三:返回一个实现了 thenable 接口的对象
如果async函数返回一个对象,并且这个对象实现了thenable接口(也就是有一个then方法),那么async函数返回的Promise对象的状态和值,就会由这个对象的then方法来决定。比如说:
javascript
const thenableObject = {
// 定义then方法,接收resolve和reject两个函数作为参数
then: function (resolve, reject) {
setTimeout(() => {
// 2秒后调用resolve函数,将值设为"通过thenable对象返回的值"
resolve('通过thenable对象返回的值');
}, 2000);
}
};
async function getThenable() {
// 返回实现了thenable接口的对象thenableObject
return thenableObject;
}
// 调用getThenable函数,它返回一个Promise对象
getThenable().then((result) => {
// 2秒后打印"通过thenable对象返回的值"
console.log(result);
});
在这段代码中,thenableObject是一个实现了thenable接口的对象。getThenable这个async函数返回了这个对象。当我们调用getThenable函数时,它返回的Promise对象的状态和值,由thenableObject的then方法决定。2 秒后,then方法中的resolve函数被调用,值为"通过thenable对象返回的值",所以在then回调函数中打印出这个值。
通过上面几种情况,我们可以看出,async函数无论返回什么,最终都会返回一个Promise对象。这也使得我们可以用处理Promise的方式来处理async函数的返回值,为我们进行异步操作带来了极大的便利。
await 关键字的作用
await关键字是和async函数紧密配合使用的,它只能在async函数内部使用。await关键字的主要作用是暂停async函数的执行,直到它等待的那个异步操作完成(也就是对应的Promise对象变为resolved状态),然后获取这个异步操作的结果(也就是Promise对象的resolved值)。
await 等待一个 Promise 对象
当await后面跟着一个Promise对象时,async函数会在这里暂停执行,直到这个Promise对象的状态变为resolved,然后await会返回这个Promise对象的resolved值。咱们来看个例子:
javascript
function fetchUserData() {
// 返回一个Promise对象,模拟网络请求获取用户数据,1秒后将状态变为resolved,值为{ name: '张三', age: 25 }
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: '张三', age: 25 });
}, 1000);
});
}
async function displayUserData() {
// await等待fetchUserData函数返回的Promise对象,暂停执行,1秒后获取到用户数据
const user = await fetchUserData();
// 打印用户数据{ name: '张三', age: 25 }
console.log(user);
}
// 调用displayUserData函数
displayUserData();
在这段代码中,fetchUserData函数返回一个Promise对象,模拟网络请求获取用户数据。在displayUserData这个async函数中,await fetchUserData();这行代码会暂停displayUserData函数的执行,直到fetchUserData函数返回的Promise对象状态变为resolved。1 秒后,Promise对象状态变为resolved,值为{ name: '张三', age: 25 },await获取到这个值并赋值给user变量,然后打印出用户数据。
await 等待一个普通值
如果await后面跟着的不是一个Promise对象,而是一个普通值,那么await会直接返回这个普通值,async函数也不会暂停执行。例如:
javascript
async function printNumber() {
// await直接返回普通数字456
const num = await 456;
// 打印数字456
console.log(num);
}
// 调用printNumber函数
printNumber();
在这段代码中,await 456;会直接返回456,printNumber函数继续执行,打印出456。
await 与错误处理
在使用await时,我们还需要注意错误处理。因为await等待的Promise对象有可能会变为rejected状态,如果不处理这种情况,程序就会报错。我们可以使用try - catch语句来捕获await可能抛出的错误。比如:
function fetchUserData() {
// 返回一个Promise对象,模拟网络请求失败,1秒后将状态变为rejected,值为"网络请求失败"
return new Promise((_, reject) => {
setTimeout(() => {
reject('网络请求失败');
}, 1000);
});
}
async function displayUserData() {
try {
// await等待fetchUserData函数返回的Promise对象,暂停执行,1秒后Promise变为rejected状态,抛出错误
const user = await fetchUserData();
// 如果Promise变为resolved状态,才会执行这里,由于上面Promise是rejected状态,这里不会执行
console.log(user);
} catch (error) {
// 捕获到错误,打印"错误信息:网络请求失败"
console.log('错误信息:', error);
}
}
// 调用displayUserData函数
displayUserData();
在这段代码中,fetchUserData函数返回的Promise对象在 1 秒后变为rejected状态,值为"网络请求失败"。在displayUserData函数中,await fetchUserData();这行代码会抛出错误,这个错误被try - catch语句捕获,在catch块中打印出错误信息。 通过await关键字,我们可以以一种类似同步代码的方式来编写异步操作,让异步代码看起来更加简洁、易读。它就像是给异步操作加上了一个 "暂停键",让我们可以更好地控制异步操作的执行顺序和结果获取。
async 函数内部的执行流程详细剖析
了解了async函数和await关键字的基本概念后,接下来我们深入剖析一下async函数内部的执行流程。
没有 await 的 async 函数
当一个async函数内部没有await关键字时,它的执行流程其实和普通函数非常相似,都是按照代码的顺序依次执行。只不过async函数会返回一个Promise对象。例如:
javascript
async function simpleAsyncFunction() {
// 打印"开始执行async函数内部代码"
console.log('开始执行async函数内部代码');
// 打印"async函数内部代码执行完毕"
console.log('async函数内部代码执行完毕');
// 返回一个普通值"返回结果"
return '返回结果';
}
// 调用simpleAsyncFunction函数,它返回一个Promise对象
simpleAsyncFunction().then((result) => {
// 打印"返回结果"
console.log(result);
});
在这段代码中,simpleAsyncFunction是一个async函数,内部没有await关键字。当我们调用这个函数时,首先会执行console.log('开始执行async函数内部代码');,然后执行console.log('async函数内部代码执行完毕');,最后返回"返回结果"。由于它是async函数,返回值会被包裹在一个已解决(resolved)状态的Promise对象中。所以在then回调函数中,我们可以获取到这个返回值并打印出来。整个过程就像普通函数一样,按照顺序执行,只是返回值的处理方式有所不同。
有 await 的 async 函数 当async函数内部有await关键字时,情况就变得稍微复杂一些了。我们来看一个具体的例子:
javascript
function task1() {
// 返回一个Promise对象,模拟一个耗时2秒的任务,2秒后将状态变为resolved,值为"任务1完成"
return new Promise((resolve) => {
setTimeout(() => {
resolve('任务1完成');
}, 2000);
});
}
function task2() {
// 返回一个Promise对象,模拟一个耗时1秒的任务,1秒后将状态变为resolved,值为"任务2完成"
return new Promise((resolve) => {
setTimeout(() => {
resolve('任务2完成');
}, 1000);
});
}
async function main() {
// 打印"开始执行async函数main"
console.log('开始执行async函数main');
// await等待task1函数返回的Promise对象,暂停执行,2秒后获取到结果
const result1 = await task1();
// 打印"任务1的结果:任务1完成"
console.log('任务1的结果:', result1);
// await等待task2函数返回的Promise对象,暂停执行,1秒后获取到结果
const result2 = await task2();
// 打印"任务2的结果:任务2完成"
console.log('任务2的结果:', result2);
// 打印"async函数main执行完毕"
console.log('async函数main执行完毕');
// 返回一个包含两个任务结果的数组
return [result1, result2];
}
// 调用main函数,它返回一个Promise对象
main().then((finalResult) => {
// 打印最终结果 ["任务1完成", "任务2完成"]
console.log('最终结果:', finalResult);
});
在这个例子中,main
是一个async
函数,内部有两个await
关键字。下面我们详细分析一下它的执行流程:
-
调用
main
函数 :当我们调用main
函数时,它会返回一个Promise
对象。此时,main
函数内部的代码开始执行,首先打印出"开始执行async函数main"
。 -
遇到第一个
await
:执行到const result1 = await task1();
时,await
会暂停main
函数的执行,同时task1
函数会被调用。task1
函数返回一个Promise
对象,模拟一个耗时2秒的任务。在这2秒内,main
函数会一直处于暂停状态,不会继续执行下面的代码。 -
第一个
Promise
对象状态变为resolved
:2秒后,task1
函数返回的Promise
对象状态变为resolved
,值为"任务1完成"
。await
获取到这个值,并将其赋值给result1
变量。此时,main
函数恢复执行,打印出"任务1的结果: 任务1完成"
。 -
遇到第二个
await
:接着执行到const result2 = await task2();
,await
再次暂停main
函数的执行,task2
函数被调用。task2
函数返回一个Promise
对象,模拟一个耗时1秒的任务。在这1秒内,main
函数暂停执行。 -
第二个
Promise
对象状态变为resolved
:1秒后,task2
函数返回的Promise
对象状态变为resolved
,值为"任务2完成"
。await
获取到这个值,并将其赋值给result2
变量。main
函数恢复执行,打印出"任务2的结果: 任务2完成"
。 -
main
函数执行完毕 :继续执行下面的代码,打印出"async函数main执行完毕"
,然后返回一个包含两个任务结果的数组[result1, result2]
。由于main
是async
函数,这个返回值会作为其返回的Promise
对象的resolved
值。 -
处理
main
函数返回的Promise
对象 :在main().then((finalResult) => {...})
中,then
回调函数会在main
函数返回的Promise
对象状态变为resolved
时执行,打印出最终结果["任务1完成", "任务2完成"]
。
从这个例子可以看出,await
关键字让async
函数的执行流程变得更像是同步代码,我们可以按照顺序依次处理多个异步任务,避免了使用回调函数嵌套带来的"回调地狱"问题。
结合宏任务和微任务理解执行流程
在实际的执行过程中,async
函数和await
关键字的执行还和宏任务、微任务的执行机制密切相关。我们来看一个更复杂的例子:
javascript
// 打印"开始执行整体代码(宏任务)"
console.log('开始执行整体代码(宏任务)');
// 创建一个Promise对象,立即执行其回调函数
new Promise((resolve) => {
// 打印"Promise内部同步代码执行"
console.log('Promise内部同步代码执行');
// 调用resolve函数,将Promise状态变为resolved
resolve();
})
// 当Promise状态变为resolved时,执行then回调函数,这是一个微任务
.then(() => {
// 打印"Promise then回调函数执行,这是微任务"
console.log('Promise then回调函数执行,这是微任务');
});
// 定义一个async函数
async function asyncFunction() {
// 打印"开始执行async函数内部代码"
console.log('开始执行async函数内部代码');
// 返回一个Promise对象,模拟一个耗时1秒的任务,1秒后将状态变为resolved,值为"async函数返回结果"
return new Promise((resolve) => {
setTimeout(() => {
resolve('async函数返回结果');
}, 1000);
});
}
// 调用async函数,它返回一个Promise对象
asyncFunction().then((result) => {
// 1秒后打印"async函数返回结果:async函数返回结果"
console.log('async函数返回结果:', result);
});
// 打印"整体代码(宏任务)执行完毕"
console.log('整体代码(宏任务)执行完毕');
下面我们来分析一下这个例子的执行流程:
-
开始执行整体代码(宏任务) :首先打印出
"开始执行整体代码(宏任务)"
。 -
执行
Promise
内部同步代码 :遇到new Promise
,其内部的同步代码console.log('Promise内部同步代码执行');
立即执行,然后调用resolve
函数,将Promise
状态变为resolved
。此时,then
回调函数作为微任务被放入微任务队列中。 -
调用
asyncFunction
函数 :调用asyncFunction
函数,它返回一个Promise
对象。asyncFunction
函数内部的console.log('开始执行async函数内部代码');
会立即执行,然后返回一个模拟耗时1秒的Promise
对象。 -
继续执行整体代码 :打印出
"整体代码(宏任务)执行完毕"
。此时,当前宏任务执行完毕,开始检查微任务队列。 -
执行微任务 :发现微任务队列中有
Promise
的then
回调函数,执行它,打印出"Promise then回调函数执行,这是微任务"
。 -
等待
asyncFunction
返回的Promise
对象状态变化 :1秒后,asyncFunction
返回的Promise
对象状态变为resolved
,其then
回调函数被执行,打印出"async函数返回结果: async函数返回结果"
。
通过这个例子,我们可以更清楚地看到async
函数和await
关键字(这里虽然没有await
,但async
函数返回Promise
对象的处理和有await
时的原理相关)在宏任务和微任务的执行机制下是如何工作的。
前端开发中async函数和await关键字的实际应用场景
网络请求
在前端开发中,网络请求是非常常见的操作。比如从服务器获取用户信息、商品列表等。使用async
函数和await
关键字可以让我们更方便地处理网络请求的结果。例如,使用fetch
API进行网络请求:
javascript
async function fetchData() {
try {
// await等待fetch函数返回的Promise对象,暂停执行,直到请求完成
const response = await fetch('https://api.example.com/data');
// 检查响应状态是否为200
if (!response.ok) {
throw new Error('网络请求失败');
}
// await等待response.json()返回的Promise对象,暂停执行,直到解析JSON数据完成
const data = await response.json();
// 打印解析后的数据
console.log('获取到的数据:', data);
return data;
} catch (error) {
// 捕获到错误,打印错误信息
console.error('请求出错:', error);
}
}
// 调用fetchData函数
fetchData();
在这个例子中,fetchData
是一个async
函数。await fetch('https://api.example.com/data');
会暂停函数执行,直到网络请求完成。如果请求成功,再使用await response.json();
暂停函数执行,直到JSON数据解析完成。这样,我们可以以同步的方式处理异步的网络请求,代码看起来更加简洁易懂。
处理多个异步任务的顺序执行
有时候,我们需要按照一定的顺序依次执行多个异步任务。例如,先从服务器获取用户的基本信息,再根据这些信息获取用户的详细信息。使用async
函数和await
关键字可以很方便地实现这一点。
javascript
function getUserBasicInfo() {
// 返回一个Promise对象,模拟获取用户基本信息的请求,1秒后将状态变为resolved,值为{ id: 1, name: '李四' }
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: 1, name: '李四' });
}, 1000);
});
}
function getUserDetailInfo(userId) {
// 返回一个Promise对象,模拟获取用户详细信息的请求,1秒后将状态变为resolved,值为{ id: 1, age: 30, address: '北京市' }
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: userId, age: 30, address: '北京市' });
}, 1000);
});
}
async function getFullUserInfo() {
// await等待getUserBasicInfo函数返回的Promise对象,暂停执行,1秒后获取到用户基本信息
const basicInfo = await getUserBasicInfo();
// 打印用户基本信息 { id: 1, name: '李四' }
console.log('用户基本信息:', basicInfo);
// await等待getUserDetailInfo函数返回的Promise对象,暂停执行,1秒后获取到用户详细信息
const detailInfo = await getUserDetailInfo(basicInfo.id);
// 打印用户详细信息 { id: 1, age: 30, address: '北京市' }
console.log('用户详细信息:', detailInfo);
// 返回一个包含用户基本信息和详细信息的对象
return { ...basicInfo, ...detailInfo };
}
// 调用getFullUserInfo函数
getFullUserInfo().then((fullInfo) => {
// 打印用户完整信息 { id: 1, name: '李四', age: 30, address: '北京市' }
console.log('用户完整信息:', fullInfo);
});
在这个例子中,getFullUserInfo
是一个async
函数。它先使用await
等待getUserBasicInfo
函数返回的Promise
对象,获取用户基本信息。然后根据基本信息中的id
,使用await
等待getUserDetailInfo
函数返回的Promise
对象,获取用户详细信息。这样就实现了多个异步任务的顺序执行。
处理动画效果的顺序播放
在前端开发中,有时候我们需要按照一定的顺序依次播放多个动画效果。使用async
函数和await
关键字可以很好地控制动画的播放顺序。例如,使用CSS
动画和JavaScript
结合:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.box {
width: 100px;
height: 100px;
background-color: blue;
margin-bottom: 20px;
}
@keyframes slideIn {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>
</head>
<body>
<div class="box" id="box1"></div>
<div class="box" id="box2"></div>
<script>
async function playAnimations() {
const box1 = document.getElementById('box1');
const box2 = document.getElementById('box2');
// 定义一个函数,返回一个Promise对象,当动画结束时将状态变为resolved
function animate(element, animationName, duration) {
return new Promise((resolve) => {
element.style.animation = `${animationName} ${duration}s forwards`;
element.addEventListener('animationend', () => {
resolve();
}, { once: true });
});
}
// await等待box1的slideIn动画完成
await animate(box1, 'slideIn', 1);
// await等待box2的fadeIn动画完成
await animate(box2, 'fadeIn', 1);
console.log('所有动画播放完毕');
}
// 调用playAnimations函数
playAnimations();
</script>
</body>
</html>
在这个例子中,playAnimations
是一个async
函数。它定义了一个animate
函数,用于创建一个Promise
对象,当动画结束时将该Promise
对象的状态变为resolved
。然后使用await
依次等待box1
的slideIn
动画和box2
的fadeIn
动画完成,从而实现了动画效果的顺序播放。
总结
async
函数和await
关键字是JavaScript中处理异步操作的强大工具。async
函数返回一个Promise
对象,让我们可以用处理Promise
的方式来处理其返回值。await
关键字则可以暂停async
函数的执行,直到等待的异步操作完成,以一种类似同步代码的方式编写异步代码,避免了"回调地狱"问题。
通过深入理解async
函数内部的执行流程,以及结合宏任务、微任务的执行机制,我们可以更好地控制异步操作的执行顺序和结果获取。在前端开发中,async
函数和await
关键字在网络请求、处理多个异步任务的顺序执行、处理动画效果的顺序播放等场景中都有广泛的应用。
希望通过本文的内容介绍,你对async
函数和await
关键字有了更深入的理解,能够在实际开发中更加熟练地运用它们,编写出更加简洁、高效的异步代码。