import invariant from "invariant"; import TinyQueue from "tinyqueue"; // Push promises onto a queue with a priority. // Run a given number of jobs in parallel // Useful for prioritizing network requests export default class LoadQueue { constructor({ threads }) { // TODO: Consider not running items with zero priority // Priority is a function so that items can change their priority between // when their priority is evaluated. // For example, we might add a track to the playlist and then scroll to/away // from it before it gets processed. this._queue = new TinyQueue([], (a, b) => a.priority() - b.priority()); this._availableThreads = threads; } push(task, priority) { const t = { task, priority }; this._queue.push(t); // Wait until the next event loop to pick a task to run. This way, we can // enqueue multiple items in an event loop, and be sure they will be run in // priority order. setTimeout(() => { this._run(); }, 0); return () => { // TODO: Could return a boolean representing if the task has already been // kicked off. this._queue = this._queue.filter(t1 => t1 !== t); }; } _run() { while (this._availableThreads > 0) { if (this._queue.length === 0) { return; } this._availableThreads--; const t = this._queue.pop(); const promise = t.task(); invariant( typeof promise.then === "function", `LoadQueue only supports loading Promises. Got ${promise}` ); promise.then(() => { this._availableThreads++; this._run(); }); } } }