如何用jQuery Promise执行后台任务?同步函数Promise化遇阻求助
问题分析与解决方案
我来帮你捋清楚问题出在哪,以及怎么解决~
为什么当前输出顺序不符合预期?
你的validateCell函数虽然用了jQuery.Deferred包装,但里面的大循环是同步阻塞代码。也就是说,当你调用this.validateCell(cell)时,这个循环会直接在当前主线程里从头到尾执行完,才会继续后面的逻辑。哪怕你返回了Promise,这个Promise在函数执行完的瞬间就已经resolve了,所以$.when的done回调会同步触发,自然就出现了entering! 0 1 finished! out!的顺序。
怎么改成你想要的异步顺序?
要实现你期望的输出,核心是把耗时的同步操作放到异步队列里执行,让主线程先完成当前的同步代码(也就是console.log("out!")),再去处理耗时任务。最简单的方式是用setTimeout把循环包裹起来,示例代码如下:
public Bind(cell: number) { console.log("entering!"); $.when(this.validateCell(cell)).done( () => { console.log("finished!"); }); console.log("out!"); } private validateCell(cell: number): JQueryPromise<any> { const dfd = $.Deferred(); console.log("0"); // 将耗时操作放入异步队列,不阻塞当前主线程 setTimeout(() => { for (let i = 0; i < 1000000000; i++) { const t = i; } console.log("1"); dfd.resolve(); }, 0); // 0毫秒表示尽快执行,但会等当前事件循环结束后再触发 return dfd.promise(); }
修改后的执行逻辑:
- 调用
Bind,首先打印entering! - 调用
validateCell,打印0,然后把循环的回调放到异步队列,立即返回Promise - 主线程继续执行
console.log("out!") - 当前事件循环的同步代码执行完毕,开始处理异步队列里的循环任务
- 循环执行完成后打印
1,resolve Deferred - 触发
done回调,打印finished!
这样输出顺序就变成了你想要的:entering! out! 0 1 finished!
额外提示
如果你的实际场景是计算量极大的密集型任务,setTimeout虽然能解决顺序问题,但还是会占用主线程导致页面卡顿。这种情况下可以考虑使用Web Worker来在后台线程处理耗时任务,完全不阻塞主线程。不过如果只是为了调整执行顺序,上面的setTimeout方案就足够啦。
内容的提问来源于stack exchange,提问作者kevinob
相关产品推荐
相关产品推荐

