一、Promise 的出现背景
1. 回调地狱问题
在 Promise 出现之前,JavaScript 异步编程主要依赖回调函数,导致:
- 嵌套层级深:多个异步操作形成"金字塔"结构
- 错误处理困难:需要在每个回调中单独处理错误
- 流程控制复杂:难以实现复杂的异步逻辑组合
// 典型的回调地狱
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
console.log(d);
}, failureCallback);
}, failureCallback);
}, failureCallback);
}, failureCallback);
2. Promise 的解决方案
Promise 提供了:
- 链式调用:通过
.then()方法扁平化异步流程 - 统一的错误处理:通过
.catch()集中处理错误 - 状态不可逆:确保异步操作结果的一致性
二、Promise 核心概念
1. 三种状态
- pending:初始状态,既不是成功也不是失败
- fulfilled:操作成功完成
- rejected:操作失败
2. 基本用法
const promise = new Promise((resolve, reject) => {
// 异步操作
if (/* 成功 */) {
resolve(value);
} else {
reject(error);
}
});
promise
.then(value => { /* 成功处理 */ })
.catch(error => { /* 失败处理 */ });
三、Promise API 详解
1. 构造函数
new Promise(executor);
executor:执行函数,接收resolve和reject两个参数- 立即执行,在构造函数中抛出错误会导致 Promise 拒绝
2. 实例方法
then()
promise.then(
onFulfilled?: (value: any) => any,
onRejected?: (error: any) => any
) => Promise
- 返回新 Promise,支持链式调用
- 参数可选,省略时值会透传
catch()
promise.catch(
onRejected?: (error: any) => any
) => Promise
- 相当于
.then(null, onRejected) - 捕获链上所有错误
finally()
promise.finally(
onFinally?: () => void
) => Promise
- 无论成功失败都会执行
- 不接收参数,不影响最终值
3. 静态方法
Promise.resolve()
Promise.resolve(value) => Promise
- 创建已解决的 Promise
- 如果参数是 Promise,则直接返回
Promise.reject()
Promise.reject(reason) => Promise
- 创建已拒绝的 Promise
Promise.all()
Promise.all(iterable) => Promise
- 所有 Promise 成功时返回结果数组
- 任何一个失败立即拒绝
Promise.allSettled()
Promise.allSettled(iterable) => Promise
- 等待所有 Promise 完成(无论成功失败)
- 返回包含状态和结果的对象数组
Promise.race()
Promise.race(iterable) => Promise
- 取最先完成的 Promise 结果(无论成功失败)
Promise.any()
Promise.any(iterable) => Promise
- 取最先成功的 Promise 结果
- 全部失败时返回 AggregateError
四、Promise 高级特性
1. 值透传
当 .then 缺少处理函数时,值会直接传递到下一个 .then:
Promise.resolve(1)
.then(2)
.then()
.then(console.log); // 输出 1
2. 错误冒泡
错误会沿着链一直传递,直到被 .catch 捕获:
Promise.reject(new Error('fail'))
.then(val => console.log(val))
.then(val => console.log(val))
.catch(err => console.error(err)); // 捕获错误
3. 返回新 Promise
.then 中可以返回新 Promise,后续处理会等待其完成:
Promise.resolve()
.then(() => new Promise(resolve =>
setTimeout(() => resolve('done'), 1000)
))
.then(console.log); // 1秒后输出 "done"
4. 同步抛出与异步拒绝
- 构造函数中同步抛出错误会导致 Promise 拒绝
- 在
.then中同步抛出会转换为拒绝的 Promise
new Promise(() => { throw new Error('sync error'); })
.catch(console.error); // 捕获同步错误
Promise.resolve()
.then(() => { throw new Error('async error'); })
.catch(console.error); // 捕获异步错误
五、Promise 使用模式
1. 顺序执行
function executeSequentially(promises) {
return promises.reduce((chain, promise) => {
return chain.then(() => promise);
}, Promise.resolve());
}
2. 超时控制
function timeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
)
]);
}
3. 重试机制
function retry(fn, times, delay) {
return new Promise((resolve, reject) => {
const attempt = (n) => {
fn().then(resolve)
.catch(err => {
if (n === 0) return reject(err);
setTimeout(() => attempt(n - 1), delay);
});
};
attempt(times);
});
}
4. 并发控制
async function parallelWithLimit(tasks, limit) {
const results = [];
const executing = [];
for (const task of tasks) {
const p = task().then(r => {
executing.splice(executing.indexOf(p), 1);
return r;
});
results.push(p);
executing.push(p);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
六、Promise 与 async/await
1. 基本转换
// Promise 风格
function fetchData() {
return fetch(url)
.then(response => response.json())
.catch(console.error);
}
// async/await 风格
async function fetchData() {
try {
const response = await fetch(url);
return response.json();
} catch (err) {
console.error(err);
}
}
2. 注意事项
await会暂停函数执行,直到 Promise 完成async函数总是返回 Promise- 并行操作应使用
Promise.all
// 顺序执行(慢)
async function sequential() {
const a = await task1();
const b = await task2();
return a + b;
}
// 并行执行(快)
async function parallel() {
const [a, b] = await Promise.all([task1(), task2()]);
return a + b;
}
七、Promise 实现原理
1. 简易实现
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
} else if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
} else {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
}
});
return promise2;
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 实现解析过程
}
2. Promises/A+ 规范要点
- then 方法必须返回 Promise
- 值穿透:如果
onFulfilled/onRejected不是函数必须忽略 - 异步执行:
then的回调必须异步执行 - 递归解析:处理 thenable 对象
八、最佳实践与常见错误
1. 最佳实践
- 总是返回 Promise:确保链式调用不中断
- 错误处理:每个 Promise 链都应有
.catch - 命名 Promise:调试时更有意义
- 避免冗余嵌套:扁平化 Promise 链
2. 常见错误
错误1:忘记 return
// 错误:第二个 then 接收 undefined
Promise.resolve()
.then(() => { doSomething(); })
.then(result => console.log(result));
// 正确
Promise.resolve()
.then(() => doSomething())
.then(result => console.log(result));
错误2:忽略错误处理
// 错误:未处理的拒绝
function fetchData() {
return fetch(url).then(r => r.json());
}
// 正确
function fetchData() {
return fetch(url)
.then(r => r.json())
.catch(err => {
console.error(err);
throw err; // 继续传递错误
});
}
错误3:过度嵌套
// 错误:不必要的嵌套
function getData() {
return fetch(url1).then(r1 => {
return fetch(url2).then(r2 => {
return fetch(url3).then(r3 => {
return [r1, r2, r3];
});
});
});
}
// 正确:扁平化
function getData() {
return fetch(url1)
.then(r1 => fetch(url2).then(r2 => [r1, r2]))
.then(([r1, r2]) => fetch(url3).then(r3 => [r1, r2, r3]));
}
// 更好:使用 async/await
async function getData() {
const r1 = await fetch(url1);
const r2 = await fetch(url2);
const r3 = await fetch(url3);
return [r1, r2, r3];
}
九、浏览器兼容性与 Polyfill
1. 兼容性
- 现代浏览器全面支持
- IE11 及以下不支持,需要 polyfill
2. 推荐 Polyfill
- es6-promise
- core-js
- bluebird(功能更丰富的实现)
3. 使用方式
<!-- 使用 es6-promise -->
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4.2.8/dist/es6-promise.auto.min.js"></script>
<script>
// 现在可以使用 Promise
</script>
十、总结
Promise 是 JavaScript 异步编程的基础,提供了:
- 更清晰的异步代码结构:通过链式调用替代回调嵌套
- 更好的错误处理:集中捕获异步错误
- 强大的组合能力:通过
Promise.all/race等组合异步操作 - async/await 的基础:使异步代码看起来像同步代码