JavaScript异步编程:Promise、async与await详解
本文详细解析JavaScript中的异步编程机制,从同步与异步的基本概念入手,深入讲解Promise的三种状态及all、race、any方法的区别。通过案例演示如何利用async和await简化异步代码,解决回调地狱问题,并展示批量请求的优化技巧,帮助开发者提升代码可读性与执行效率。
一、JS里的同步异步
fun1();
fun2();
fun3();参考上述代码,在同步执行模式下,fun1、fun2和fun3三个函数会按照书写顺序依次执行,即fun1执行完毕后才会执行fun2,最后执行fun3。
异步执行则意味着多个任务可以并行进行,彼此之间互不阻塞,无需相互等待。
需要注意的是,虽然任务是并行处理的,但这并不意味着执行顺序会混乱。JavaScript引擎依然保持顺序执行的逻辑,只是fun1尚未完成时,fun2即可开始执行。
JavaScript中常见的异步函数包括settimeout、fetch和setInterval等。
二、Promise
Promise在中文里意为“承诺”,在JavaScript中可以通俗地理解为:将任务(函数)交给它,由它负责完成。
1、状态
Promise对象拥有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。一旦状态从pending转变为fulfilled或rejected,这种改变是不可逆的。
const p=new Promise((resolve,reject)=>{
resolve("你的成功结果") //执行更改后Promise状态变成fulfilled
//reject("失败的结果") //执行更改后Promise状态变成rejected
//throw 1 //抛出异常和reject的效果一样
})
p.then(res=>res)
//then直接返回会返回包含返回结果的Promise对象,可以通过.then()继续获取
.then(res=>{
//res对应resolve里的值
console.log("fulfilled状态时打印")
//以下执行可以返回状态为rejected的Promise给下一级继续执行
return new Promise((resolve,reject)=>reject("执行出错"))
}).catch(error=>{
//error对应reject里的内容
console.log("rejected状态时打印")
}).finally(()=>{
console.log("无论成功还是失败都会打印")
})2、all()、race()、any()
首先用一句话概括Promise.all()、Promise.race()和Promise.any()的区别:
(1) Promise.all()要求序列中的所有Promise都执行成功,整个序列才被视为成功;只要有一个失败,整个序列即被视为失败。
(2) Promise.race()中,序列里第一个执行完毕的Promise决定了整个序列的结果。如果第一个执行完毕的是fulfilled,则整体成功;如果是rejected,则整体失败。
(3) Promise.any()中,只要序列中有一个Promise执行成功,整个序列即被视为成功;只有当所有Promise都失败时,整体才被视为失败。
通过一张图来说明all()和any()的行为:

any方法与all类似,主要区别在于any只要有一个Promise成功,就返回该成功的结果。
通过一张图来说明race()的行为:

如图所示,Both resolve, but promise2 is faster,这表明race()类似于赛跑。Promise.race([p1, p2, p3])会返回最先完成的那个Promise的结果,无论该结果是成功还是失败。
3、简单案例
通过以下简单案例来了解Promise的具体用法:
//Promise用法
const param1 = "normal";
const param2 = "error"; // 第二种情况
//1. 创建Promise类的对象
const promise01 = new Promise(function customFun(successFun, failFun) {
if (param1 === "normal") {
successFun("自定义成功的函数执行了!")
} else { //失败和成功的逻辑都是自定义的
failFun("自定义失败的函数执行了!")
}
})
const promise02 = new Promise(function customFun(successFun, failFun) { // 第二种情况
if (param2 === "normal") {
successFun("自定义成功的函数执行了!")
} else { //失败和成功的逻辑都是自定义的
failFun("自定义失败的函数执行了!")
}
})
//2. 执行Promise的返回结果
promise01.then(result => {
console.log(result)
}).catch(error => {
console.log(error)
})
promise02.then(result => { // 第二种情况
console.log(result)
}).catch(error => {
console.log(error)
})可以看到控制台打印如下:

第一种情况会执行自定义成功的逻辑,第二种情况则执行自定义失败的逻辑。
4、异步执行案例
//异步常见案例
const promise03 = new Promise(() => {
console.log("This is Promise constructor.")
setTimeout(() => {
console.log("Timeout executed!")
}, 3000)
})
promise03.then(result => {
console.log(result)
})
console.log("Test Demo.")控制台输出:

可以清晰地看到,在Promise被实例化时,构造函数的信息就会立即打印。随后,代码顺序执行“Test Demo”,等待3秒后,系统再执行setTimeout中的逻辑。
5、解决异步嵌套繁琐的场景
//Promise来规范Timeout嵌套调用的阅读性差问题
const promise04 = new Promise((successFun, failFun) => {
setTimeout(() => {
successFun("timeout 333")
}, 3000)
})
const promise05 = promise04.then((res) => {
console.log(res)
return new Promise((successFun, failFun) => {
setTimeout(() => {
successFun("timeout 222")
}, 2000)
})
})
promise05.then(res => {
console.log(res)
})
console.log("Test Demo.")控制台打印:

可以看到,首先打印了“Test Demo”,3秒后执行了第一个Promise中的内容。执行完第一个Promise后,再过2秒执行了第二个Promise中的内容。相比setTimeout的多层嵌套,这种方式的可读性更好。
三、async和await
async关键字必须用于修饰函数,表示该函数是一个异步函数。await关键字必须与async配合使用,专门用于修饰Promise的实例化对象。
1、async返回类型
//async有返回类型时,以Promise形式返回
async function asyncDemo01Fun(){
return "Demo01"
}
async function asyncDemo02Fun(){
return Promise.resolve("Demo02")
}
console.log(asyncDemo01Fun())
console.log(asyncDemo02Fun())打印结果:

可以看到,直接返回结果和利用Promise.resolve返回结果的效果是一样的。
由async修饰的函数,即使有返回值,也会直接返回一个Promise对象,而不是直接返回值。
2、async与await结合使用的简单案例
//async和await的案例Demo
async function asyncFun1() {
const p = new Promise((successFun, failFun) => {
setTimeout(() => {
successFun({num: 1})
}, 3000)
})
let obj = await p;
console.log(obj)
}
asyncFun1()
console.log("Test Demo.")打印结果:

可以看到,当有await修饰Promise返回对象时,代码会等待该Promise执行完成后,再返回结果给调用者,最终将获取到的对象值在3秒后打印。
当有await修饰Promise对象时,系统会等待返回值后再继续执行后续代码。实际上,这是将异步处理的事件处理完成后再继续执行同步代码。
3、解决异步嵌套问题
//async和await的案例Demo
async function asyncFun2() {
const p1 = new Promise((successFun, failFun) => {
setTimeout(() => {
successFun("Demo 333")
}, 3000)
})
let obj1 = await p1;
console.log(obj1)
const p2 = new Promise((successFun, failFun) => {
setTimeout(() => {
successFun("Demo 222")
}, 2000)
})
let obj2 = await p2;
console.log(obj2)
}
asyncFun2()
console.log("Test Demo.")控制台打印:

可以看到,首先打印了“Test Demo”,3秒后执行了第一个Promise中的内容。执行完第一个Promise后,再过2秒执行了第二个Promise中的内容。相比setTimeout的多层嵌套,这种方式的可读性更好。
4、批量请求优化
以下是同步获取请求结果的案例:
const batchReq=async ()=>{
const url="https://example.com/api/demo"
let rep1=await fetch(`${url}/1/`)
let json1=await rep1.json()
let name1=await json1.data.name
let rep2=await fetch(`${url}/2/`)
let json2=await rep1.json()
let name2=await json1.data.name
let rep3=await fetch(`${url}/3/`)
let json3=await rep1.json()
let name3=await json1.data.name
console.log(name1)
console.log(name2)
console.log(name3)
}以上代码在执行过程中,由于加了await进行修饰,请求会依次进行,即1执行完2再执行,最后3执行。
如要实现批量请求同时处理,可以参考以下代码:
const batchReq = async () => {
try {
const url = "https://example.com/api/demo"
let resp = await Promise.all([fetch(`${url}/1/`), fetch(`${url}/2/`), fetch(`${url}/3/`)])
let jsons = resp.map(res => res.json())
let values = await Promise.all(jsons)
values.map(value => {
console.log(value.data.name)
})
} catch (error) {
console.log(error)
}
}这样写便可以批量发送请求了。
总结
到此这篇关于JavaScript异步Promise、Async、await使用的文章就介绍到这了,更多相关JS异步Promise、Async、await内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
