javascript 上抢占式后台工作调度的通用解决方案

General solution for pre-emptive background work scheduling on javascript

场景如下: 当我的网络应用程序启动时,我想从本地存储中的几个 table 加载数据(使用 indexedDB)。我将这项工作委托给网络工作者。它将依次加载每个 table,并在加载每个数据时触发一条包含数据的消息。在主线程上,侦听器将接收消息并将数据存储在缓存中。

但假设用户按下按钮查看特定 table 的数据。该应用程序调用一个检查缓存的函数,发现 table 的数据尚未加载。

此函数如何等待 table 的数据被缓存,以便它可以 return 数据?更重要的是,如果 table 被安排在最后加载怎么办?此函数如何向 Web Worker 发送消息以优先加载特定 table 以便其数据尽快可用?

此抢占式调度问题的干净解决方案的一般模式是什么?如果可能的话,我想避免轮询。

Worker 可能会使用一个异步队列,其中包含所有要加载的 tables 并按特定优先级排序,因此您可以优先处理某些 tables 并将它们排序到table 前面。由于您还没有展示真正的实现,这里是一个更通用的版本:

 class AsyncPriorityQueue {
   constructor(task){
     this.task = task;
     this.queue = [];
   }

   push(element, priority = 0){
     const pos = this.queue.findIndex(el => el.priority < priority) + 1;
     this.queue.splice(pos, 0, {element, priority});

     if(this.running) return;
     this.running = true;
     this._run();
   }

   prioritize(element, priority = 10){
     const pos = this.queue.findIndex(el => el.element === element);
     if(pos != -1) this.queue.splice(pos, 1);

     this.push(element, priority);
  }

   async _run(){
     while(this.queue.length)
        await this.task(this.queue.shift().element);
  }
}

注意:如果任务不是异步的,您应该使用类似 setTimeout(next, 0) 的方式来允许进程消息传递中断它...


示例实现可以是图像加载器:

 class ImageLoader extends AsyncPriorityQueue  {
   constructor(){
     super(function task(url){
       const img = new Image();
       img.src = url;
       return new Promise(res => img.onload = res);
     });
   }
}

const loader = new ImageLoader;

 loader.push("a.jpg");
 loader.push("b.jpg", 1); // a bit more important
 // Oh, wait:
 loader.prioritize("a.jpg");