视频加载失败

异步async和await

2018 字
10 分钟
异步async和await

async和await使基于Promise的异步操作更简洁方便,Promise是解决回调地狱问题,async和await则是使异步的代码看起来像同步的代码,更容易理解

async和await是什么#

可以声明一个async函数,表示函数里面有异步操作,一般只有在async函数中才能使用await;

await异步等待,表示紧跟在await后面的是异步的,需要等待结果,await需要和async一起使用

基本语法:

async function fn() {
const result = await 异步操作(Promise对象)
}

为什么用?使异步操作更简洁方便;使异步代码看起来像同步,更容易理解

使用async 和 await#

实现一个效果,一个广告图片一开始是消失的,然后隔几秒显示,然后又消失

回调函数方式:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.ad{
display: block;
width: 300px;
height: 150px;
margin: 0 auto;
}
.none{
display: none;
}
</style>
</head>
<body>
<img src="./ad.jpg" alt="" class="ad none" id="ad">
<script>
// 回调函数
function ad () {
// 获取DOM
const $ad = document.getElementById('ad');
setTimeout(() => {
// 1S后移除
$ad.classList.remove('none')
setTimeout(() => {
// 2S后隐藏
$ad.classList.add('none')
}, 2000)
}, 1000)
}
ad()
</script>
</body>
</html>

Promise方式

function wait (ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
function ad () {
const $ad = document.getElementById('ad');
wait(1000).then(() => {
// 1S后显示ad
$ad.classList.remove('none')
return wait(2000)
}).then(() => {
// 2S后隐藏ad
$ad.classList.add('none')
return wait(3000)
}).then(() => {
// 3S后重新显示ad
$ad.classList.remove('none')
})
}
ad()

async方式

function wait (ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function ad () {
const $ad = document.getElementById('ad');
await wait(1000)
// 1S后显示ad
$ad.classList.remove('none')
await wait(2000)
// 2S后隐藏ad
$ad.classList.add('none')
await wait(3000)
// 3S后重新显示ad
$ad.classList.remove('none')
}
ad()

async函数的返回值#

async函数的返回值是Promise对象,观察:

async function fn () {
return 'hello world'
//相当于
return Promise.resolve('hello world')
}
console.log(fn());

async函数的各种形式#

函数声明:

async function fn () {}

函数表达式

const fn = async function(){}

箭头函数

const fn = async()=>{}

箭头函数单参数

const fn = async param => {}

对象方法

const obj = {
fn:async function(){},
fn1:async ()=>{},
async fn(){}
}

class的方法

class ClassName{
async fn(){}
}

async函数的注意事项#

async函数返回值是一个成功状态的Promise,如果需要失败的,需要手动设置

第一种方式:使用Promise.reject

async function fn () {
// return 123
return Promise.reject(new Error('出错了'))
}
fn().catch(err => {
console.log(err);
})
console.log(fn());

第二种方式,使用throw new Error,当然,这没必要可以去报错,如果逻辑错误了,Promise就是失败态

async function fn () {
// return 123
// return Promise.reject(new Error('出错了'))
try {
throw new Error('出错了')
} catch (error) {
console.log(error);
}
}
console.log(fn());

async函数可以没有await,反过来则不行

await的机制#

沿用之前广告显示隐藏的例子

function wait (ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function ad () {
const $ad = document.getElementById('ad');
await wait(1000)
// 1S后显示ad
$ad.classList.remove('none')
await wait(2000)
// 2S后隐藏ad
$ad.classList.add('none')
await wait(3000)
// 3S后重新显示ad
$ad.classList.remove('none')
}
ad()

这里的await wait(1000)相当于阻塞了1秒后才执行后面的代码,相当于以下代码

wait(1000).then(() => {
// 1S后显示ad
$ad.classList.remove('none')
return wait(2000)
}).then(() => {
// 2S后隐藏ad
$ad.classList.add('none')
return wait(3000)
}).then(() => {
// 3S后重新显示ad
$ad.classList.remove('none')
})

结论:async函数中的代码有先后关系,await会阻塞该async函数中代码的执行,注意:await 确实会暂停 async 函数后续代码的执行,但它不会阻塞整个 JavaScript 主线程

await的值#

await 表达式的返回值,取决于它后面跟的是什么。核心规则:await 会等待 Promise 完成,并取出它成功完成时的结果;如果后面不是 Promise,则直接得到该值。

await 后面的值await 表达式的结果
Promise.resolve(100)100
Promise.reject('失败')抛出 '失败'
100100
'hello''hello'
{ age: 18 }{ age: 18 }
fetch('/api/data')请求成功后得到 Response 对象

await的注意事项#

  1. await 只能在 async 函数中使用;注意:ES 模块还支持在函数外使用顶层 await。
async function fn() {
const result = await Promise.resolve(100);
console.log(result);
}
fn();
  1. await 只暂停当前函数的后续执行;await 不会阻塞整个 JavaScript 主线程,其他代码仍然可以执行。
async function fn() {
console.log(1);
await Promise.resolve();
console.log(2);
}
fn();
console.log(3);
  1. await 后面可以跟普通值,不一定是 Promise;如果后面不是 Promise 或其他 thenable 对象,await 会将其作为已经完成的值处理。
async function fn() {
console.log(await 100); // 100
console.log(await 'hello'); // hello
}
fn();
  1. await 遇到 rejected Promise 会抛出错误;因此,等待可能失败的异步操作时,可以使用 try...catch 处理错误。
async function fn() {
try {
const result = await Promise.reject('请求失败');
} catch (error) {
console.log(error); // 请求失败
}
}
fn();
  1. 多个 await 默认按顺序等待
async function fn() {
const a = await task1();
const b = await task2();
}

如果 task2() 必须等待 task1() 完成后才能开始,这样写是合适的。

但如果两个任务相互独立,这样写会让它们依次执行,可以改为:

async function fn() {
const [a, b] = await Promise.all([
task1(),
task2()
]);
}

这样两个任务可以同时启动,不必等待第一个任务完成后才启动第二个。

  1. 不要误以为 await 会自动等待所有异步任务:例如,forEach() 不会等待异步回调全部执行完毕:
async function fn() {
[1, 2, 3].forEach(async item => {
await task(item);
});
console.log('执行完毕');
}

这里的 '执行完毕' 不会等待所有 task() 完成。

如果需要依次执行,使用 for...of:

async function fn() {
for (const item of [1, 2, 3]) {
await task(item);
}
console.log('全部执行完毕');
}

如果需要并发执行并等待全部完成,可以使用 Promise.all()。

速记表#

注意事项核心要点
使用位置async 函数中,或 ES 模块顶层
执行机制暂停当前异步函数的后续执行,不阻塞主线程
等待对象可以是 Promise,也可以是普通值
错误处理Promise 被拒绝时,await 会抛出错误
多个任务有依赖就顺序等待,互相独立可考虑 Promise.all()
循环处理forEach() 不会等待异步回调完成

其中,最值得掌握的是顺序等待与并发等待的区别,以及 await 的错误处理。这两点在实际开发中尤其常用。

继发和并发#

继发(顺序执行)前一个任务完成后,才开始执行下一个任务。

async function fn() {
const a = await task1();
const b = await task2();
console.log(a, b);
}

并发(同时启动):多个任务不必等待彼此完成,可以同时启动,然后统一等待结果。

async function fn() {
const p1 = task1();
const p2 = task2();
const [a, b] = await Promise.all([p1, p2]);
console.log(a, b);
}

项目应用实战#

二级菜单#

初始化代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>二级菜单</title>
<style>
/* css reset */
* {
padding: 0;
margin: 0;
}
li {
list-style: none;
}
/* menu */
.menu {
width: 100px;
background-color: rgba(0, 0, 0, 0.1);
margin: 10px;
}
.menu-item {
position: relative;
padding: 5px;
cursor: pointer;
}
.menu-content {
display: none;
position: absolute;
left: 100%;
top: 0;
width: 200px;
height: 100px;
padding: 0 5px;
background-color: rgba(0, 0, 0, 0.1);
}
.menu-item:hover {
background-color: rgba(0, 0, 0, 0.4);
}
.menu-item:hover .menu-content {
display: block;
}
.menu-loading {
margin: 45px 0 0 92px;
}
</style>
</head>
<body>
<ul id="menu" class="menu">
<!-- <li class="menu-item" data-key="hot" data-done="done">
<span>热门出发地</span>
<div class="menu-content">
<img class="menu-loading" src="./loading.gif" alt="加载中" />
<p>内地热门城市</p>
<p>热门景点</p>
</div>
</li> -->
</ul>
<script>
// https://www.imooc.com/api/mall-PC/index/menu
// https://www.imooc.com/api/mall-PC/index/menu/hot
// https://www.imooc.com/api/mall-PC/index/menu/hk
function wait(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
function getJSON(url) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.addEventListener(
'load',
async () => {
if (
(xhr.status >= 200 && xhr.status < 300) ||
xhr.status === 304
) {
// 模拟网络延迟
await wait(1000);
resolve(xhr.response);
}
},
false
);
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.send(null);
});
}
</script>
</body>
</html>

实现:

(async function () {
const url = 'https://www.imooc.com/api/mall-PC/index/menu'
// 发送请求
const menuResult = await getJSON(url);
const $menu = document.getElementById('menu')
console.log(menuResult);
createMenu($menu, menuResult.data);
// 循环绑定事件,发送请求,获取数据,创建子菜单
const $item =$menu.getElementsByClassName('menu-item');
[...$item].forEach($item => {
$item.addEventListener('mouseenter', async () => {
if ($item.dataset.done === 'done') return;
const subMenuResul = await getJSON(`https://www.imooc.com/api/mall-PC/index/menu/${$item.dataset.key}`)
$item.dataset.done = 'done'
console.log(subMenuResul);
createsubMenu($item, subMenuResul.data)
},false)
});
})()
function createMenu ($el, data) {
let html = ''
for (const item of data) {
html += `
<li class="menu-item" data-key="${item.key}">
<span>${item.title}</span>
<div class="menu-content">
<img class="menu-loading" src="./loading.gif" alt="加载中" />
`
$el.innerHTML = html
}
}
function createsubMenu ($el, data) {
let html = ''
for (const item of data) {
html += `
<p>${item.title}</p>
`
}
$el.querySelector('.menu-content').innerHTML = html
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

异步async和await
https://f.youzi177.cn/posts/2026102206-yi-bu-async-he-await/
作者
小封鸭
发布于
2026-10-22
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
小封鸭
小楼一夜听春雨,深巷明朝卖杏花
公告
欢迎来到我的博客。这里主要记录前端全栈和日常内容
分类
标签
最新动态
站点统计
文章
60
分类
3
标签
12
总字数
107,734
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录