如何让JS函数A和B并行运行且兼容IE11、不阻塞UI交互?
并行执行异常根因
仅为函数添加async关键字无法实现同步耗时逻辑的并行执行:async仅会将函数返回值包装为Promise,函数内部的同步阻塞代码仍然会在主线程按调用顺序执行,前一个函数的同步逻辑未执行完成时,后续代码会被阻塞,自然出现A完全执行完才启动B的现象。同时主线程被长时间占用时,UI的鼠标、键盘事件响应也会被阻塞。
兼容Chrome/ Firefox/ IE11的后台并行实现方案
有两种成熟方案可选,可根据任务类型选择:
- Web Worker方案:适合CPU密集型的长时间连续任务
IE11已完整支持基础Web Worker能力,可将A、B两个函数的逻辑封装到独立的Worker线程文件中,主线程同时启动两个Worker实例即可实现真正的并行执行,运行过程完全不占用主线程资源,不会阻塞UI响应。该方案的限制为Worker线程无法直接操作DOM,线程间数据传递需要通过结构化克隆实现。 - 空闲时间分片方案:适合可拆分的轻量任务,无多线程开销
即你自研的zParallel类插件的实现思路,底层优先调用requestIdleCallbackAPI利用浏览器空闲时间执行任务,IE11不支持该API时用setTimeout做降级模拟。将大任务拆分为多个执行耗时小于16ms的小分片,在空闲时间段分批执行,不会抢占UI响应的时间片,自然不会阻塞鼠标键盘操作。
参考实现代码
错误async用法示例(可复现你遇到的执行顺序问题)
async function funcA() { // 同步耗时循环 for(let i=0;i<1e9;i++){} console.log('A执行完成') } async function funcB() { console.log('B执行完成') } funcA() funcB() // 固定输出顺序:A执行完成 -> B执行完成
兼容IE11的空闲分片调度核心实现
var zParallel = (function(){ // IE11降级实现requestIdleCallback var requestIdle = window.requestIdleCallback || function(cb){ var start = Date.now(); return setTimeout(function(){ cb({ didTimeout: false, timeRemaining: function(){ return Math.max(0, 50 - (Date.now() - start)); } }); }, 0); }; var cancelIdle = window.cancelIdleCallback || clearTimeout; function TaskQueue() { this.tasks = []; this.isRunning = false; } // 添加任务到队列 TaskQueue.prototype.add = function(task) { this.tasks.push(task); if (!this.isRunning) this.run(); }; // 执行队列任务 TaskQueue.prototype.run = function() { var self = this; self.isRunning = true; requestIdle(function(deadline){ // 空闲时间耗尽或队列空之前持续执行 while(deadline.timeRemaining() > 0 && self.tasks.length > 0){ var currentTask = self.tasks.shift(); currentTask(); } if (self.tasks.length > 0) { self.run(); } else { self.isRunning = false; } }); }; return { createQueue: function(){ return new TaskQueue(); } } })();
调度插件用法示例
// 创建两个独立队列实现A、B并行调度 var queueA = zParallel.createQueue(); var queueB = zParallel.createQueue(); // 插入函数A的分片任务 for(var i=0; i<1000; i++){ queueA.add(function(){ // 函数A的单分片业务逻辑 }) } queueA.add(function(){ console.log('A全部执行完成') }); // 插入函数B的分片任务 for(var i=0; i<1000; i++){ queueB.add(function(){ // 函数B的单分片业务逻辑 }) } queueB.add(function(){ console.log('B全部执行完成') });
内容的提问来源于stack exchange,提问作者Mykola Uspalenko
相关产品推荐
相关产品推荐

