JavaScript现代异步方案——Promise、async / await
Promise是一种用于处理异步操作的容器对象,相比回调函数具有显著优势:可读性强(避免回调地狱)、错误处理统一(catch捕获)、流程控制灵活(all/race等方法)以及明确的状态管理(pending/fulfilled/rejected)。Promise状态不可逆,通过then处理成功状态,catch捕获异常,支持链式调用。链式调用中then的返回值决定下一个Promise的状态,推荐使用c
文章目录
Promise
promise优势
Promise 对象是一个代理对象,通俗点讲可以把它理解为一个容器。它的参数是个回调函数,该回调接收一个resolve和reject函数,异步任务成功的结果绑定至resolve函数,失败的结果绑定至reject函数。
Promise 出现的核心背景是解决回调函数的固有弊端
| 对比 | Promise | 回调函数 |
|---|---|---|
| 可读性 | 嵌套层级深(回调地狱),结构混乱 | 链式调用 可读性强 |
| 错误处理 | 无统一处理入口,容易遗漏 | 统一通过 catch 捕获所有层级错误 |
| 流程控制 | 需嵌套多层,逻辑繁琐 | 可通过 Promise.all / allSettled / race 控制 |
| 状态管理 | 无状态 | 有明确三种状态(pending / fulfilled / rejected) |
Promise状态
一个 Promise 对象具备三种状态
- pending: Promise新创建时的初始状态,还未执行 reslove 和 reject 方法
const pending_promise = new Promise((resolve, reject) => {
})
console.log(pending_promise)

- resolved/fulfilled: Promise对象在回调函数中调用了resolve方法后的状态
const resolve_promise = new Promise((resolve, reject) => {
resolve('resolve_promise')
})
console.log(resolve_promise)

- rejected: Promise对象在回调函数中调用了reject 方法后的状态
const reject_promise = new Promise((resolve, reject) => {
reject('reject_promise')
})
console.log(reject_promise)

- Promise状态转换是不可逆的,一旦从pending 状态转换至resolved/rejected,就不能再回到pending状态。
Promise状态控制
Promise就是一个容器,将异步任务包裹在这个容器里即可,异步任务成功和失败的结果分别赋给给 resolve 函数和 reject函数。
then
Promise.then () 方法接收两个可选参数,分别用于处理 Promise 成功和失败的情况:
promise.then(onFulfilled, onRejected)
- onRejected 参数与单独使用 catch () 效果类似,但 catch () 更常用(更清晰)
// 只处理成功
promise.then(result => console.log('成功:', result))
// 同时处理成功和失败
promise.then(
result => console.log('成功:', result),
error => console.log('失败:', error)
)
// 链式调用
promise
.then(result => result + 1)
.then(result => console.log('最终结果:', result))
.catch(error => console.log('任何环节出错:', error))
- promise读取图像
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const err = new Error(`图片加载失败${url}`)
reject(err)
}
img.src = url
})
}
// 正确的图片地址
const url1 = 'https://profile.csdnimg.cn/4/8/2/3_qq_37987033'
// 错误的图片地址
const url2 = 'https://profile.csdnimgxx.cn/4/8/2/3_qq_37987033-error'
使用 promise 的 then 方法能够获取状态为 resolved 时的对象,并进行后续处理。
then方法接收的是一个函数,该函数参数为promise执行 resolve 时带过来的值,如果传入的是其它的东西,then方法不会触发。
loadImg(url1).then(img => {
console.log(img)
})

catch捕获异常
使用 promise 的 **catch** 方法能够获取状态为 **rejected** 时的错误并抛出
loadImg(url2).catch((err) => {
console.error(err)
})

链式调用
promise在使用时允许链式调用
-
当前一个 promise 的状态为 resolved 时,会跳过 catch 进入下一个
then方法;当前一个promise的状态为 rejected 时,会跳过 then 进入下一个catch方法 -
在使用链式调用时,then 方法中需要
一个返回值作为下个 then 的输入, return 值的 3 种情况:
情况 1:return 普通值(字符串、数字、对象等)→ 新 Promise 变为 fulfilled,后续 then 接收该普通值。
情况 2:return 一个全新的 Promise_A → 新 Promise 的状态由 Promise_A 决定,后续 then 等待其状态变化后执行。
情况 3:不 return 任何值 → 等同于 return undefined,新 Promise 变为 fulfilled,后续 then 接收 undefined。
- 无论是then方法还是catch方法,只要它们
执行正常,返回值都会变成resolved状态;若执行过程中出错,就会变成rejected状态
// 简单链式调用(贴合前端接口请求场景)
new Promise((resolve) => {
resolve(1); // 初始Promise变为fulfilled,值为1
})
.then(res => {
console.log(res); // 输出1
return res + 1; // return普通值,新Promise值为2
})
.then(res => {
console.log(res); // 输出2
// return一个新Promise
return new Promise((resolve) => {
setTimeout(() => resolve(res + 1), 1000);
});
})
.then(res => {
console.log(res); // 1秒后输出3(等待前一个return的Promise完成)
throw new Error("测试错误"); // 抛出错误,触发后续catch
})
.then(
res => console.log("不会执行"), // 被跳过
err => console.log("失败回调", err) // 可捕获错误,也可用catch
)
.catch(err => {
console.log("最终错误捕获", err); // 若上面then未写失败回调,此处捕获
});
执行结果:
分析:
- 在第三个 then 函数中,由于 throw new Error ,将promise的状态改为了 reject,在第四个 then 中,命中了异常场景的处理, 打印出:"失败回调 3 "
- 更推荐使用catch统一捕获:
// 简单链式调用(贴合前端接口请求场景)
new Promise((resolve) => {
resolve(1); // 初始Promise变为fulfilled,值为1
})
.then(res => {
console.log(res); // 输出1
return res + 1; // return普通值,新Promise值为2
})
.then(res => {
console.log(res); // 输出2
// return一个新Promise
return new Promise((resolve) => {
setTimeout(() => resolve(res + 1), 1000);
});
})
.then(res => {
console.log(res); // 1秒后输出3(等待前一个return的Promise完成)
throw new Error("测试错误"); // 抛出错误,触发后续catch
})
.then(
res => console.log("不会执行"), // 被跳过
// err => console.log("失败回调", err) // 可捕获错误,也可用catch
)
.catch(err => {
console.log("最终错误捕获", err); // 若上面then未写失败回调,此处捕获
});

Promise的异步时机
promise在 new 时就会执行同步方法, resolve包裹而成的 Promise 对象在执行
then时才是异步的。
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve('resolve1');
console.log(2);
resolve('resolve2') // 状态只能改变一次
console.log(3);
});
// 异步
promise.then((res) => {
console.log(4,res);
});
console.log(5);

Promise流程处理
// 成功的promise
const promise1 = new Promise((resolve, reject) => {
resolve('promise1')
})
// 成功的promise
const promise2 = new Promise((resolve, reject) => {
resolve('promise2')
})
// 失败的promise
const promise3 = new Promise((resolve, reject) => {
reject('promise3')
})
all
- Promise.all 接收一个Promise对象数组,对象数组里所有的 promise 对象
都成功的时候才会触发成功;只要有一个失败(rejected),立即返回该失败原因,其余 Promise 停止执行。
// promise.all 全部成功
Promise.all([promise1, promise2]).then(res => {
console.log(res);
});
// promise.all promise3失败成功
Promise.all([promise1, promise2, promise3]).then(res => {
console.log(res);
}).catch(err=>{
console.error(err)
})
结果:
allSettled
- Promise.all 接收一个Promise对象数组,无论所有 Promise 是成功还是失败,都会等待所有操作全部完成,返回一个包含所有结果的数组(每个结果对象包含状态和对应值 / 错误)
Promise.allSettled([promise1, promise2, promise3]).then((results) => {
// 遍历结果,区分成功/失败
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("成功:", result.value);
} else {
console.log("失败:", result.reason);
}
});
});
运行结果:
race
- Promise.race 接收一个Promise对象数组 race方法返回
第一个执行完毕的 promise 的结果,该结果可以被then 或者 catch捕获,其余的promise会被忽略
const promiseRace1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("promiseRace1");
}, 500);
});
const promiseRace2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("promiseRace2");
}, 600);
});
const promiseRace3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("promiseRace3");
}, 700);
});
Promise.race([promiseRace1, promiseRace2, promiseRace3]).then((results) => {
console.log("race成功:", results);
}).catch((err) => {
console.error("race失败:", err);
});
执行结果:
使用场景
- all / allSettled / race
| 方法 | 规则 | catch处理 |
|---|---|---|
| all | 全部成功才返回,一个失败则终止 | 立即返回失败原因,其余停止 |
| allSettled | 无论成败,等待全部完成,返回所有结果 | 不终止,收集所有失败原因 |
| race | 最先完成的返回(无论成败) | 最先失败则返回失败原因 |
- 场景: 前端首屏渲染前,需同时请求「用户信息接口」和「首页列表接口」,两个接口必须全部成功,才能渲染页面
- 方法: all
// 模拟接口请求(真实开发中替换为fetch/axios)
const getUserInfo = () => new Promise(resolve => {
setTimeout(() => resolve({ id: 1, name: '张三' }), 1000); // 1秒后返回用户信息
});
const getHomeList = () => new Promise(resolve => {
setTimeout(() => resolve([{ id: 101, title: '首页文章1' }]), 800); // 800毫秒返回列表
});
// 用Promise.all并行请求,全部成功才渲染
Promise.all([getUserInfo(), getHomeList()])
.then(([userInfo, homeList]) => {
console.log('页面初始化完成');
console.log('用户信息:', userInfo);
console.log('首页列表:', homeList);
// 执行页面渲染逻辑(仅当两个接口都成功才执行)
})
.catch(err => {
console.log('页面加载失败:', err); // 只要一个接口失败,直接提示失败
});
- 场景: 用户批量上传图片,无需等待所有图片上传成功,哪怕部分失败,也要继续上传其余图片,最终统计 “成功几张、失败几张”,反馈给用户
- 方法: allSettled
// 模拟图片上传(随机成功/失败,模拟真实上传异常)
const uploadImg = (imgName) => new Promise((resolve, reject) => {
setTimeout(() => {
// 随机模拟成功/失败(真实开发中根据接口响应判断)
Math.random() > 0.5
? resolve(`${imgName} 上传成功`)
: reject(`${imgName} 上传失败(网络异常)`);
}, 1000);
});
// 批量上传3张图片,收集所有结果
Promise.allSettled([
uploadImg('图片1.png'),
uploadImg('图片2.png'),
uploadImg('图片3.png')
])
.then(results => {
let successCount = 0;
let failCount = 0;
// 遍历所有结果,统计成败
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log(result.value);
successCount++;
} else {
console.log(result.reason);
failCount++;
}
});
// 反馈给用户
console.log(`上传完成:成功${successCount}张,失败${failCount}张`);
});
- 场景: 避免接口长期无响应导致页面卡死,设置「5 秒超时时间」—— 若 5 秒内接口未响应,直接提示 “请求超时”;若 5 秒内接口返回,正常处理数据
- 方法: race
// 模拟商品详情接口(随机模拟响应快慢)
const getGoodsDetail = () => new Promise(resolve => {
// 随机响应时间(1-6秒),模拟接口慢响应场景
const delay = Math.random() * 5000 + 1000;
setTimeout(() => resolve({ id: 202, name: '手机', price: 3999 }), delay);
});
// 模拟超时提示(5秒后触发失败)
const timeoutTip = () => new Promise((_, reject) => {
setTimeout(() => reject(new Error('请求超时,请重试!')), 5000);
});
// 用race实现超时控制:接口和超时,谁先完成返回谁
Promise.race([getGoodsDetail(), timeoutTip()])
.then(goodsDetail => {
console.log('商品详情:', goodsDetail);
// 渲染商品详情页面
})
.catch(err => {
console.log(err.message); // 要么超时,要么接口失败,均提示错误
// 显示超时/失败提示,允许用户重新请求
});
async、await
async / await 和Promise的关系
- await需要被一个async函数包裹,后面跟一个promise对象,它们的存在消灭了
异步回调。 - 执行 async 函数,返回的是
Promise对象,如果不是,它会自动把返回值包裹成一个promise对象 - await 相当于 Promise 的
then;它会暂停 async 函数的执行,直到 Promise 状态改变 try...catch可以捕获异常,代替了Promise 的catch
async / await 本身是Promise的语法糖,它让原本的链式调用变成了真正的同步写法。除此之外,async / await 可以使用 try/catch 捕获异常,这是它的一大优势。
async / await 使用
- async/await 读取图像
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const err = new Error(`图片加载失败${url}`)
reject(err)
}
img.src = url
})
}
// 正确的图片地址
const url1 = 'https://profile.csdnimg.cn/4/8/2/3_qq_37987033'
// 错误的图片地址
const url2 = 'https://profile.csdnimgxx.cn/4/8/2/3_qq_37987033-error'
async function readImg() {
try {
const img1 = await loadImg(url1)
console.log(img1.width, img1.height)
const img2 = await loadImg(url2)
console.log(img2.width, img2.height)
} catch (error) {
console.log(error)
}
}
readImg()
输出如下👇
all 并行执行请求
// 模拟接口请求(真实开发中替换为fetch/axios)
const getUserInfo = () => new Promise(resolve => {
setTimeout(() => resolve({ id: 1, name: '张三' }), 1000); // 1秒后返回用户信息
});
const getHomeList = () => new Promise(resolve => {
setTimeout(() => resolve([{ id: 101, title: '首页文章1' }]), 800); // 800毫秒返回列表
});
try {
const [userInfo, homeList] = await Promise.all([getUserInfo(), getHomeList()]);
// 执行页面渲染逻辑
} catch {
console.error('Error fetching data:', error);
}
异步(微任务)
微任务指的是ES6之后和 Promise 相关的所有语法:
- Promise、
- async/await,
- process.nextTick 在微任务中优先级最高
深入理解:
- 在Promise 中 只有 then 中执行的是异步
- 在async / await 在 await 下一行起执行的是异步
async function async1 () {
console.log('async1 start') // 同步 2
await async2() // 同步
console.log('async1 end') // 7 await 后面的内容,异步(微任务),相当于 callback 函数里的内容
}
async function async2 () {
console.log('async2') // 3
}
console.log('script start') //1
async1() // 同步
new Promise (function (resolve) {
console.log('promise1') // 同步 4
resolve()
}).then (function () {
console.log('promise2') // 8
})
process.nextTick(() => { //6
console.log('nextTick')
})
console.log('script end') // 同步完成 5
结果:

更多推荐


所有评论(0)