大家好,我是CC,在这里欢迎大家的到来~
背景
为什么会有这样的感慨呢?
是这样,最近业务上对批量审核的功能进行了优化,由原先的循环挨个请求改成了并发模式,提高了效率;但是紧接着上线后发现在一个特殊业务上批量审核后总有漏掉的,排查发现是该业务下有“Group”的概念,当批量审核同“Group”下的内容时并发导致资源抢占,而且超时时间设置较短,在事务回滚后部分审核没有完成。
问题找到了如何解决呢。当然可以让后端同事处理,但是细想后前端可以三行代码轻松解决这个问题。
于是就发现 Promise 与 Web Locks 配合的妙处。
Promise
异步任务
JavaScript 的本质上是单线程的,因此在任何时刻,只有一个任务会被执行,尽管控制权可以在不同的 Promise 之间切换,从而使 Promise 的执行看起来是并发的。
在 JavaScript 中,并行执行只能通过 worker 线程实现。
如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled)。
并发方法
Promise.all() - 在所有传入的 Promise 都被兑现时兑现;在任意一个 Promise 被拒绝时拒绝。
Promise.allSettled() - 在所有的 Promise 都被敲定时兑现。
Promise.any() - 在任意一个 Promise 被兑现时兑现;仅在所有的 Promise 都被拒绝时才会拒绝。
Promise.race() - 在任意一个 Promise 被敲定时敲定。换句话说,在任意一个 Promise 被兑现时兑现;在任意一个的 Promise 被拒绝时拒绝。
其他常用方法
Promise.resolve() - 返回一个新的 Promise 对象,该对象以给定的原因拒绝。
Promise.reject() - 返回一个新的 Promise 对象,该对象以给定的值兑现。
Web Locks
之前文章有讲述过,这里跳转复习下吧。
结合使用场景
现在我们实现下开头说的场景需求:
// 伪代码
const taskFns = tasks.map((task) => {
return () => {
return new Promise((resolve, reject) => {
const requestId = task.groupId === "" ? `updateStatus-${task?.id}` : `updateStatus-${task.groupId}`;
// 三行代码搞定
navigator.locks.request(requestId, async() => {
try {
// 其他同步/异步操作
} catch(err) {
// 抛出err
}
resolve(true);
});
});
};
});
async function limitRun(taskFns, limit = 10) {
const results = [];
const executing = [];
for (const taskFn of taskFns) {
const p = Promise.resolve().then(taskFn);
results.push(p);
executing.push(p);
// 任务完成后从执行池移除
p.finally(() => {
const idx = executing.indexOf(p);
if (idx !== -1) executing.splice(idx, 1);
});
// 超过并发上限则等待任意一个先完成
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
await limitRun(taskFns, 10);
当时除了并发请求场景会使用到,也有其他场景下可以使用 Web Locks。比如数据流或媒体流的处理、多任务执行等等场景下。
总结
Promise 和 Web Locks 的结合,可以更轻松高效的解决一些场景下的难题;后续会在业务中持续探索他们的使用场景,与大家分享。
6323

被折叠的 条评论
为什么被折叠?



