基於promise的阻塞式佇列設計

H_H_code發表於2021-12-29

設計阻塞佇列的緣由是因為在做業務時遇見龐大的資源列表載入問題,我們的業務主要是媒體資源的載入。http 1.0的時候大部分瀏覽器支援最大並行數量是6個,http 2.0的時候有所提升,HTTP/2.0理論上可以在一個TCP連線上傳送無數個HTTP請求。
如果我們一次載入100張圖片,那麼網路資源搶佔嚴重。如果使用者網路環境差,那麼資源載入問題就更加嚴重了。那麼我們可以考慮 控制並行的數量,每次資源載入的數量控制,控制載入佇列始終在6個以內。
為了解決這個問題,設計了基於promise的阻塞式佇列。本質就是一個佇列長度有限的生產消費模式。以圖片為例子:

class Scheduler {

    constructor(maxCount = 6) {
        this.list = [];
        this.count = 0;
        this.maxCount = maxCount;
    }

    // 新增任務佇列
    addTask(promiseCreator) { 
        return new Promise((resolve) => {
            // 新增的時候通知 佇列有東西可以消費
            this.list.push(() => {
                return promiseCreator().then(res => {
                    this.count -= 1;
                    // 一個任務做完下一個任務才開始
                    this.consume();
                    resolve(res);
                    return res;
                });
            });
            this.consume();
        });
    }

    provider = (src) => {
        return this.addTask(() => new Promise((resolve, reject) => {
            const img = new  Image();
            img.src = src;
            img.onload = function() {
                // 圖片載入成功
                resolve(src);
            }
            img.onerror = function(err) {
                // 圖片載入失敗
                reject(err);
            }
        }));
    };

    consume() {
        // 最多同時做兩個任務
        if(this.count < this.maxCount && this.list.length > 0) {
            this.count += 1;
            const item = this.list.shift();
            item && item();
        } 
    }
}

使用:

const queue = new Scheduler();
queue.provider('http://xxxxxxxx');
queue.provider('http://xxxxxxxx');
queue.provider('http://xxxxxxxx');
queue.provider('http://xxxxxxxx');
queue.provider('http://xxxxxxxx');
queue.provider('http://xxxxxxxx');
queue.provider('http://xxxxxxxx');

相關文章