javascript异步编程指的是在javascript执行环境中,处理不会立即返回结果、需要等待一段时间才能完成的操作的技术,这类操作不会阻塞主线程的其他代码执行,常见的异步场景包括发送网络请求、读取本地文件、设置定时任务等。javascript本身是单线程语言,异步编程能让它在处理耗时操作时依然保持页面的响应能力。

javascript异步编程的核心背景
javascript的运行机制决定了它同一时间只能执行一个任务,如果所有操作都同步执行,那么遇到网络请求、定时器这类耗时操作时,页面就会出现卡顿甚至无响应的情况。早期的异步编程主要依靠回调函数实现,也就是把后续要执行的代码作为参数传入异步函数中,等操作完成后再调用这个回调函数。
但回调函数的问题在于,如果有多个异步操作需要依次执行,就会出现多层嵌套的情况,也就是常说的回调地狱,代码层级会越来越深,可读性和调试难度都会大幅上升。比如下面这种典型的回调嵌套代码:
// 回调地狱示例
function getData() {
setTimeout(() => {
console.log('第一步获取数据完成');
setTimeout(() => {
console.log('第二步处理数据完成');
setTimeout(() => {
console.log('第三步保存数据完成');
}, 1000);
}, 1000);
}, 1000);
}
getData();
Promise的基本概念
Promise是ES6规范中新增的异步编程解决方案,它把异步操作的最终完成(或失败)及其结果值用一个对象来表示。Promise有三种状态:
- pending:初始状态,既不是成功也不是失败状态
- fulfilled:操作成功完成的状态
- rejected:操作失败的状态
Promise的状态一旦发生改变,就不会再变回之前的状态,只能从pending变为fulfilled,或者从pending变为rejected。我们可以通过new Promise()的方式创建一个Promise实例,构造函数接收一个执行器函数,这个函数有两个参数,分别是resolve和reject,前者用来把Promise状态改为成功,后者用来改为失败。
使用Promise处理异步操作的基础用法
下面我们用Promise改写前面的定时任务嵌套的例子,看看代码结构的变化:
// 用Promise改写后的定时任务
function delay(time, msg) {
return new Promise((resolve, reject) => {
// 模拟异步操作,这里用setTimeout模拟耗时操作
setTimeout(() => {
console.log(msg);
// 操作成功,调用resolve改变状态
resolve();
}, time);
});
}
// 链式调用执行异步操作
delay(1000, '第一步获取数据完成')
.then(() => {
return delay(1000, '第二步处理数据完成');
})
.then(() => {
return delay(1000, '第三步保存数据完成');
})
.catch((err) => {
// 捕获整个链路上任何一步的失败
console.error('操作失败:', err);
});
可以看到通过then方法我们可以把异步操作按顺序串联起来,避免了多层嵌套的问题。then方法接收两个可选参数,第一个是成功状态的回调函数,第二个是失败状态的回调函数,不过通常我们会把失败处理统一放到catch方法中。
Promise的常用实例方法
then方法
then方法是Promise实例最核心的方法,它返回一个新的Promise实例,所以可以支持链式调用。如果then中的回调函数返回的是一个普通值,那么这个值会作为下一个then的成功回调的参数;如果返回的是一个Promise实例,那么后续的then会等待这个Promise的状态改变后再执行。
catch方法
catch方法用来捕获Promise链路上抛出的错误或者状态变为rejected的情况,它本质上是then(null, rejection)的语法糖,使用catch可以让失败处理的逻辑更清晰。
finally方法
finally方法不管Promise最终是成功还是失败都会执行,常用于清理操作,比如不管请求成功还是失败都关闭加载动画。它不会接收Promise的结果值,返回的依然是一个Promise实例。
// finally方法示例
function mockRequest() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 模拟50%概率成功
const isSuccess = Math.random() > 0.5;
if (isSuccess) {
resolve('请求成功返回的数据');
} else {
reject('请求失败的错误信息');
}
}, 1000);
});
}
mockRequest()
.then((data) => {
console.log('请求结果:', data);
})
.catch((err) => {
console.log('错误信息:', err);
})
.finally(() => {
console.log('请求完成,关闭加载动画');
});
Promise的静态方法
Promise本身还提供了几个常用的静态方法,用来处理多个Promise的场景:
| 方法名 | 作用说明 |
|---|---|
| Promise.all() | 接收一个Promise数组,所有Promise都成功才返回成功,只要有一个失败就返回失败,返回的结果是所有成功结果的数组 |
| Promise.race() | 接收一个Promise数组,只要有一个Promise状态改变,就返回这个Promise的结果,不管成功还是失败 |
| Promise.allSettled() | 接收一个Promise数组,等待所有Promise都完成(不管成功还是失败),返回每个Promise的状态和结果 |
| Promise.resolve() | 把一个值转为成功的Promise实例,如果参数本身已经是Promise则直接返回 |
| Promise.reject() | 创建一个失败的Promise实例,参数是失败的原因 |
比如我们需要同时发送多个请求,等所有请求都完成后再统一处理数据,就可以用Promise.all:
// Promise.all示例
function requestA() {
return new Promise((resolve) => {
setTimeout(() => resolve('请求A的结果'), 1000);
});
}
function requestB() {
return new Promise((resolve) => {
setTimeout(() => resolve('请求B的结果'), 1500);
});
}
Promise.all([requestA(), requestB()])
.then((results) => {
console.log('所有请求完成,结果:', results); // ["请求A的结果", "请求B的结果"]
})
.catch((err) => {
console.log('有请求失败:', err);
});
Promise结合async/await简化异步代码
ES7引入的async/await语法是基于Promise的语法糖,能让异步代码看起来更像同步代码的写法,可读性进一步提升。async用来修饰函数,被修饰的函数会返回一个Promise实例;await只能用在async函数中,用来等待一个Promise的状态改变,得到它的结果值。
前面的链式调用例子用async/await改写后会更简洁:
// async/await改写示例
function delay(time, msg) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(msg);
resolve();
}, time);
});
}
async function runTasks() {
try {
await delay(1000, '第一步获取数据完成');
await delay(1000, '第二步处理数据完成');
await delay(1000, '第三步保存数据完成');
} catch (err) {
console.error('操作失败:', err);
} finally {
console.log('所有任务执行完成');
}
}
runTasks();
需要注意的是,await后面的Promise如果变为rejected状态,错误会被try/catch捕获,所以配合try/catch可以很方便地处理异步操作的错误。这种写法几乎没有嵌套,逻辑和同步代码几乎一致,是目前前端处理异步操作的主流方式。
javascript异步编程Promise回调地狱修改时间:2026-06-21 16:15:20