Promise高级特性
本章将介绍Promise的一些高级特性和新版本ECMAScript中新增的功能,帮助您更深入地理解和使用Promise。
6.1 Promise构造函数的高级用法
Promise构造函数除了基本用法外,还有一些高级技巧可以帮助我们更好地控制异步操作。
Promise的延迟执行
通过将Promise的创建包装在函数中,可以实现延迟执行。
示例:延迟执行的Promise
// 创建延迟执行的Promise
function delayedPromise(value, delay) {
return () => new Promise(resolve => {
setTimeout(() => resolve(value), delay);
});
}
// 使用延迟执行的Promise
const delayedPromises = [
delayedPromise('第一个', 1000),
delayedPromise('第二个', 2000),
delayedPromise('第三个', 3000)
];
// 按顺序执行
delayedPromises.reduce((chain, delayedPromise) => {
return chain.then(results => {
return delayedPromise().then(result => [...results, result]);
});
}, Promise.resolve([]))
.then(results => {
console.log('按顺序执行结果:', results);
});
Promise的取消机制
虽然原生Promise不支持取消,但可以通过AbortController等机制实现取消功能。
示例:可取消的Promise
// 创建可取消的Promise
function cancellablePromise(promiseFn, signal) {
return new Promise((resolve, reject) => {
// 检查是否已经取消
if (signal.aborted) {
reject(new Error('操作已被取消'));
return;
}
// 监听取消信号
signal.addEventListener('abort', () => {
reject(new Error('操作已被取消'));
});
// 执行原始Promise
promiseFn().then(resolve).catch(reject);
});
}
// 使用可取消的Promise
const controller = new AbortController();
const { signal } = controller;
const cancellableFetch = cancellablePromise(
() => fetch('/api/data'),
signal
);
// 设置超时取消
const timeoutId = setTimeout(() => {
controller.abort();
}, 5000);
cancellableFetch
.then(response => {
clearTimeout(timeoutId);
console.log('获取数据成功:', response);
})
.catch(error => {
clearTimeout(timeoutId);
if (error.message === '操作已被取消') {
console.log('请求已被取消');
} else {
console.error('请求失败:', error);
}
});
6.2 AggregateError
AggregateError是ES2021中引入的错误类型,用于表示多个错误的集合,主要与Promise.any配合使用。
示例:AggregateError的使用
// Promise.any失败时会抛出AggregateError
const promises = [
Promise.reject(new Error('错误1')),
Promise.reject(new Error('错误2')),
Promise.reject(new Error('错误3'))
];
Promise.any(promises)
.then(result => {
// 不会执行到这里
console.log(result);
})
.catch(error => {
if (error instanceof AggregateError) {
console.log('所有Promise都失败了:');
error.errors.forEach((err, index) => {
console.log(` ${index + 1}. ${err.message}`);
});
} else {
console.error('其他错误:', error);
}
});
// 手动创建AggregateError
const errors = [
new Error('第一个错误'),
new Error('第二个错误'),
new Error('第三个错误')
];
const aggregateError = new AggregateError(errors, '多个操作失败');
console.log(aggregateError.message); // 输出: 多个操作失败
console.log(aggregateError.errors); // 输出: [Error, Error, Error]
6.3 Promise.try()提案
Promise.try是一个提案中的方法,用于同步或异步代码统一处理。
示例:Promise.try的模拟实现
// 模拟Promise.try的实现
Promise.try = function(callback) {
return new Promise((resolve, reject) => {
try {
const result = callback();
resolve(result);
} catch (error) {
reject(error);
}
});
};
// 使用Promise.try处理同步和异步代码
Promise.try(() => {
// 同步代码
if (Math.random() > 0.5) {
return '同步结果';
} else {
// 异步代码
return new Promise(resolve => {
setTimeout(() => resolve('异步结果'), 1000);
});
}
})
.then(result => {
console.log('操作结果:', result);
})
.catch(error => {
console.error('操作失败:', error);
});
6.4 Promise的性能优化
在处理大量Promise时,需要注意性能优化问题。
避免创建不必要的Promise
示例:避免不必要的Promise创建
// 不好的做法:创建不必要的Promise
function getValue(value) {
return Promise.resolve(value); // 如果value已经是确定的值
}
// 好的做法:只在必要时创建Promise
function getValue(value) {
if (value instanceof Promise) {
return value;
}
return Promise.resolve(value);
}
// 或者使用工具函数
function promisify(value) {
return value instanceof Promise ? value : Promise.resolve(value);
}
批量处理优化
示例:批量处理优化
// 优化大量Promise的处理
function optimizedBatchProcess(items, processor, concurrency = 5) {
const results = [];
const executing = [];
for (const item of items) {
const promise = Promise.resolve().then(() => processor(item)).then(result => {
results.push({ item, result });
});
executing.push(promise);
// 限制并发数
if (executing.length >= concurrency) {
await Promise.race(executing);
executing.splice(executing.findIndex(p => p === promise), 1);
}
}
await Promise.all(executing);
return results;
}
// 使用优化的批量处理
const items = Array.from({ length: 1000 }, (_, i) => i);
optimizedBatchProcess(items, async (item) => {
// 模拟异步处理
await new Promise(resolve => setTimeout(resolve, 10));
return item * 2;
}, 10)
.then(results => {
console.log('处理完成,结果数量:', results.length);
});
6.5 Promise与微任务队列
理解Promise与微任务队列的关系对于掌握JavaScript的执行机制非常重要。
示例:Promise与微任务队列
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
Promise.resolve().then(() => {
console.log('4');
});
console.log('5');
// 输出顺序: 1, 5, 3, 4, 2
// 更复杂的例子
console.log('start');
setTimeout(() => {
console.log('setTimeout1');
}, 0);
Promise.resolve().then(() => {
console.log('promise1');
return Promise.resolve();
}).then(() => {
console.log('promise2');
});
setTimeout(() => {
console.log('setTimeout2');
}, 0);
console.log('end');
// 输出顺序: start, end, promise1, promise2, setTimeout1, setTimeout2
6.6 Promise的调试技巧
调试Promise代码时有一些有用的技巧可以帮助我们更快地定位问题。
添加调试标签
示例:Promise调试标签
// 为Promise添加调试标签
function debugPromise(promise, label) {
return promise.then(
result => {
console.log(`[成功] ${label}:`, result);
return result;
},
error => {
console.error(`[失败] ${label}:`, error);
throw error;
}
);
}
// 使用调试标签
const fetchUserPromise = debugPromise(fetchUser(123), '获取用户');
const fetchPostsPromise = debugPromise(fetchUserPosts(123), '获取帖子');
Promise.all([fetchUserPromise, fetchPostsPromise])
.then(([user, posts]) => {
console.log('所有数据获取完成');
});
Promise链的可视化
示例:Promise链可视化
// 创建可追踪的Promise
function traceablePromise(promise, name) {
console.log(`[开始] ${name}`);
return promise
.then(result => {
console.log(`[完成] ${name}`);
return result;
})
.catch(error => {
console.error(`[错误] ${name}:`, error);
throw error;
});
}
// 使用可追踪的Promise
traceablePromise(
fetchUserData(userId)
.then(user => traceablePromise(
processUserData(user),
'处理用户数据'
))
.then(processedData => traceablePromise(
saveUserData(processedData),
'保存用户数据'
)),
'完整用户处理流程'
)
.then(() => {
console.log('用户处理流程完成');
})
.catch(error => {
console.error('用户处理流程失败:', error);
});
6.7 Promise与其他异步模式的结合
Promise可以与Generator、async/await等其他异步模式结合使用。
Promise与Generator
示例:Promise与Generator结合
// 使用Generator控制Promise执行
function* asyncGenerator() {
try {
const user = yield fetchUser(123);
console.log('用户:', user);
const posts = yield fetchUserPosts(user.id);
console.log('帖子:', posts);
const comments = yield fetchPostComments(posts[0].id);
console.log('评论:', comments);
return { user, posts, comments };
} catch (error) {
console.error('执行过程中出错:', error);
throw error;
}
}
// 执行Generator
function runAsyncGenerator(generator) {
return new Promise((resolve, reject) => {
const it = generator();
function handle(result) {
if (result.done) {
resolve(result.value);
return;
}
Promise.resolve(result.value)
.then(
res => handle(it.next(res)),
err => {
it.throw(err);
reject(err);
}
);
}
handle(it.next());
});
}
// 使用
runAsyncGenerator(asyncGenerator())
.then(result => {
console.log('所有数据获取完成:', result);
})
.catch(error => {
console.error('执行失败:', error);
});
6.8 Promise的未来发展方向
Promise作为JavaScript异步编程的核心,仍在不断发展和完善中。
当前提案
- Promise.try:提供统一的同步/异步代码处理方式
- Promise.prototype.finally:已在ES2018中标准化
- Promise.allSettled:已在ES2020中标准化
- Promise.any:已在ES2021中标准化
最佳实践演进
- 从回调函数到Promise
- 从Promise到async/await
- 从单一Promise到Promise组合
- 从简单错误处理到精细化错误管理
提示:随着JavaScript的发展,新的异步编程模式不断涌现,但理解Promise的核心原理仍然是掌握现代JavaScript异步编程的基础。