§ JS的Promise
1. Promise简介:
Promise 是 JavaScript 中处理异步操作的一种解决方案,相比传统的回调函数,它提供了更优雅、更强大的异步编程方式。
2. Promise基本概念:
2.1. Promise 状态:
Promise 有三种不可逆的状态:
- pending(进行中):初始状态
- fulfilled(已成功):操作成功完成
- rejected(已失败):操作失败
2.2. 创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功');
} else {
reject(new Error('操作失败'));
}
}, 1000);
});
3. Promise核心方法
3.1. .then()- 处理成功结果
promise.then(
(result) => {
console.log('成功:', result);
},
(error) => {
console.log('失败:', error);
}
);
3.2. .catch()- 处理失败结果
promise
.then(result => console.log('成功:', result))
.catch(error => console.log('失败:', error));
3.3. .finally()- 无论成功失败都会执行
promise
.then(result => console.log('成功:', result))
.catch(error => console.log('失败:', error))
.finally(() => console.log('操作完成'));
4. Promise高级用法
4.1. Promise.all()- 并行执行多个 Promise
所有 Promise 都成功时才成功,任何一个失败就立即失败
const p1 = Promise.resolve('任务1');
const p2 = Promise.resolve('任务2');
const p3 = Promise.resolve('任务3');
Promise.all([p1, p2, p3])
.then(results => {
console.log('所有任务完成:', results);
// results: ['任务1', '任务2', '任务3']
})
.catch(error => {
console.log('有任务失败:', error);
});
4.2. Promise.allSettled()- 等待所有 Promise 完成
无论成功失败,都等待所有 Promise 完成
Promise.allSettled([p1, p2, p3])
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason);
}
});
});
4.3. Promise.race()- 竞速执行
哪个 Promise 先完成就返回哪个的结果
const p1 = new Promise(resolve => setTimeout(() => resolve('p1'), 2000));
const p2 = new Promise(resolve => setTimeout(() => resolve('p2'), 1000));
Promise.race([p1, p2])
.then(result => {
console.log('最先完成:', result); // 输出: 'p2'
});
4.4. Promise.any()- 任意一个成功就成功
任意一个 Promise 成功就返回成功,全部失败才返回失败
Promise.any([p1, p2, p3])
.then(result => {
console.log('有一个成功:', result);
})
.catch(errors => {
console.log('全部失败:', errors);
});
5. Promise使用场景
5.1. 处理异步回调地狱
// 回调地狱示例
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
console.log(c);
});
});
});
// Promise 解决方案
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => console.log(c))
.catch(error => console.error(error));
5.2. 多个异步操作同步处理
// 依次执行
getUser()
.then(user => getOrders(user.id))
.then(orders => getOrderDetails(orders[0].id))
.then(details => console.log(details));
// 并行执行
Promise.all([
getUser(),
getOrders(),
getProducts()
])
.then(([user, orders, products]) => {
// 处理所有数据
});
5.3. 异步依赖处理
function getData() {
return new Promise(resolve => {
setTimeout(() => resolve('基础数据'), 1000);
});
}
function processData(data) {
return new Promise(resolve => {
setTimeout(() => resolve(`处理后的: ${data}`), 500);
});
}
getData()
.then(data => processData(data))
.then(processedData => console.log(processedData));
5.4. 统一错误处理
function apiRequest(url) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
return response.json();
});
}
// 统一错误处理
apiRequest('/api/data')
.then(data => console.log(data))
.catch(error => {
console.error('请求失败:', error);
// 可以在这里统一处理错误,如显示提示、记录日志等
});
6. Promise静态方法
6.1. Promise.resolve()- 创建已解决的 Promise
Promise.resolve('立即值')
.then(value => console.log(value)); // 输出: '立即值'
6.2. Promise.reject()- 创建已拒绝的 Promise
Promise.reject(new Error('错误信息'))
.catch(error => console.log(error.message)); // 输出: '错误信息'
7. Promise链式调用
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => {
// 数据处理
return processData(data);
})
.then(processedData => {
// 进一步处理
return saveData(processedData);
})
.then(savedResult => {
console.log('保存成功:', savedResult);
return savedResult;
})
.catch(error => {
console.error('处理失败:', error);
throw error; // 继续传递错误
});
}
8. 手写封装Promise完整版 - 遵循Promise A+规范
8.1 手写封装:
// 定义 Promise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 考点1: 状态管理 - Promise 状态不可逆
this.state = PENDING;
this.value = undefined; // 成功时的值
this.reason = undefined; // 失败时的原因
// 考点2: 回调队列 - 处理异步 then
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 考点3: 执行器中的 resolve
const resolve = (value) => {
// 考点4: 状态一旦改变就不能再变
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
// 执行所有成功的回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
// 考点5: 执行器中的 reject
const reject = (reason) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
// 执行所有失败的回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 考点6: 立即执行执行器,并进行错误捕获
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 考点7: then 方法实现(核心)
then(onFulfilled, onRejected) {
// 考点8: 值穿透 - 如果参数不是函数,则忽略
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 考点9: then 必须返回一个新的 Promise
const promise2 = new MyPromise((resolve, reject) => {
// 考点10: 微任务模拟 - 使用 queueMicrotask
const microTask = (fn, value) => {
queueMicrotask(() => {
try {
const x = fn(value);
// 考点11: 处理返回值 - 解析 Promise
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
if (this.state === FULFILLED) {
microTask(onFulfilled, this.value);
} else if (this.state === REJECTED) {
microTask(onRejected, this.reason);
} else if (this.state === PENDING) {
// 考点12: 异步情况 - 将回调存入队列
this.onFulfilledCallbacks.push(() => {
microTask(onFulfilled, this.value);
});
this.onRejectedCallbacks.push(() => {
microTask(onRejected, this.reason);
});
}
});
return promise2;
}
// 考点13: resolvePromise 方法(Promise A+ 规范核心)
resolvePromise(promise2, x, resolve, reject) {
// 考点14: 防止循环引用
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 考点15: 如果 x 是 Promise
if (x instanceof MyPromise) {
// 考点16: 根据 x 的状态调用 resolve 或 reject
x.then(
y => this.resolvePromise(promise2, y, resolve, reject),
reject
);
}
// 考点17: 如果 x 是对象或函数
else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let then;
let called = false; // 防止重复调用
try {
then = x.then;
} catch (error) {
return reject(error);
}
// 考点18: 如果是 thenable 对象
if (typeof then === 'function') {
try {
then.call(
x,
y => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
},
r => {
if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
if (called) return;
reject(error);
}
} else {
resolve(x);
}
}
// 考点19: 普通值直接 resolve
else {
resolve(x);
}
}
// 考点20: catch 方法实现
catch(onRejected) {
return this.then(null, onRejected);
}
// 考点21: finally 方法实现
finally(callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason; })
);
}
// 考点22: 静态方法 resolve
static resolve(value) {
// 如果已经是 Promise 实例,直接返回
if (value instanceof MyPromise) {
return value;
}
return new MyPromise(resolve => {
resolve(value);
});
}
// 考点23: 静态方法 reject
static reject(reason) {
return new MyPromise((_, reject) => {
reject(reason);
});
}
// 考点24: 静态方法 all
static all(promises) {
return new MyPromise((resolve, reject) => {
const results = [];
let completedCount = 0;
// 处理空数组
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => {
results[index] = value;
completedCount++;
// 所有 Promise 都完成
if (completedCount === promises.length) {
resolve(results);
}
},
// 任一 Promise 失败
reject
);
});
});
}
// 考点25: 静态方法 race
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
// 考点26: 静态方法 allSettled
static allSettled(promises) {
return new MyPromise(resolve => {
const results = [];
let settledCount = 0;
const processResult = (index, status, valueOrReason) => {
results[index] = {
status,
[status === 'fulfilled' ? 'value' : 'reason']: valueOrReason
};
settledCount++;
if (settledCount === promises.length) {
resolve(results);
}
};
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
value => processResult(index, 'fulfilled', value),
reason => processResult(index, 'rejected', reason)
);
});
});
}
// 考点27: 静态方法 any
static any(promises) {
return new MyPromise((resolve, reject) => {
const errors = [];
let rejectedCount = 0;
promises.forEach((promise, index) => {
MyPromise.resolve(promise).then(
resolve,
reason => {
errors[index] = reason;
rejectedCount++;
// 所有都失败
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
);
});
});
}
}
如何使用:
// 测试1: 基础功能
const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => resolve('success'), 1000);
});
p1.then(res => console.log('Test1:', res)); // 1秒后输出: Test1: success
// 测试2: 链式调用
new MyPromise(resolve => resolve(1))
.then(res => {
console.log('Test2-1:', res); // 输出: Test2-1: 1
return res + 1;
})
.then(res => {
console.log('Test2-2:', res); // 输出: Test2-2: 2
return new MyPromise(resolve => resolve(res + 1));
})
.then(res => {
console.log('Test2-3:', res); // 输出: Test2-3: 3
});
// 测试3: Promise.all
MyPromise.all([
MyPromise.resolve(1),
MyPromise.resolve(2),
MyPromise.resolve(3)
]).then(res => console.log('Test3:', res)); // 输出: Test3: [1, 2, 3]
// 测试4: Promise.race
MyPromise.race([
new MyPromise(resolve => setTimeout(() => resolve('fast'), 100)),
new MyPromise(resolve => setTimeout(() => resolve('slow'), 200))
]).then(res => console.log('Test4:', res)); // 输出: Test4: fast
// 测试5: 错误处理
new MyPromise((_, reject) => reject('error'))
.then(() => console.log('不会执行'))
.catch(err => console.log('Test5:', err)) // 输出: Test5: error
.finally(() => console.log('Test5: finally executed'));
// 测试6: 值穿透
MyPromise.resolve(1)
.then(2) // 2 不是函数,被忽略
.then(res => console.log('Test6:', res)); // 输出: Test6: 1
9. 面试官可能会问的问题:
Q1: Promise 的状态可以改变吗?
A: Promise 的状态一旦改变(从 pending 到 fulfilled 或 rejected)就不可再次改变。
Q2: then 方法为什么能链式调用?
A: then 方法返回一个新的 Promise,这个新 Promise 的状态由 then 中回调函数的返回值决定。
Q3: Promise 的异步如何实现?
A: 通过微任务(queueMicrotask、MutationObserver、process.nextTick 等)实现,确保在当前任务执行完后、渲染前执行。
Q4: Promise.resolve 和 new Promise(resolve => resolve(value)) 有什么区别?
A: Promise.resolve 是快捷方式,如果参数已经是 Promise,会直接返回这个 Promise。
Q5: Promise.all 和 Promise.allSettled 的区别?
A:
- all: 所有成功才成功,一个失败就失败
- allSettled: 无论成功失败,都等待所有完成
Q6: 如何处理 Promise 中的错误?
A: 通过 catch 方法捕获,或者 then 的第二个参数。注意:catch 能捕获前面所有 then 中的错误。