欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Web?Woker與主線程通信場景下對postMessage的簡潔封裝詳解

 更新時間:2023年09月20日 08:59:32   作者:泯瀧  
這篇文章主要為大家介紹了Web?Woker與主線程通信場景下對postMessage的簡潔封裝示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

Web Worker與主線程之間進行通信

使用postMessage是一種常見的方式。然而,在某些業(yè)務(wù)場景中,postMessage可能會顯得不夠簡潔,因為它涉及到手動序列化和反序列化數(shù)據(jù),以及通過事件監(jiān)聽器處理消息。以下是一些常見問題和解決方案,以簡化在Web Worker與主線程之間的通信場景中使用postMessage的問題。

結(jié)構(gòu)化克隆問題

在Web Worker與主線程之間傳輸數(shù)據(jù)時,使用postMessage()方法進行通信,瀏覽器會對傳遞的數(shù)據(jù)進行序列化和反序列化的過程,以便在不同的線程間傳遞數(shù)據(jù)。這個序列化和反序列化的過程就是結(jié)構(gòu)化克?。⊿tructured Cloning)。

結(jié)構(gòu)化克隆是一種瀏覽器內(nèi)置的序列化和反序列化算法,它可以將復(fù)雜的JavaScript對象、數(shù)組、字符串、數(shù)字、布爾值等數(shù)據(jù)類型轉(zhuǎn)換成一個可以在不同線程間傳遞的二進制數(shù)據(jù)流,然后再將這個二進制數(shù)據(jù)流反序列化為與原始數(shù)據(jù)相同的JavaScript對象。

結(jié)構(gòu)化克隆有一些特點和限制:

  • 支持的數(shù)據(jù)類型:結(jié)構(gòu)化克隆支持包括對象、數(shù)組、字符串、數(shù)字、布爾值、日期、正則表達式、Blob、File、ImageData等常見的JavaScript數(shù)據(jù)類型。但并不支持函數(shù)、Map、Set、Symbol等一些特殊的JavaScript數(shù)據(jù)類型。
  • 克隆整個對象:結(jié)構(gòu)化克隆會克隆整個對象,包括對象的所有屬性和方法。這可能會導(dǎo)致性能開銷較大,尤其是在傳輸大規(guī)模數(shù)據(jù)時。
  • 不共享內(nèi)存:結(jié)構(gòu)化克隆會生成一份完整的副本,而不是共享內(nèi)存。這意味著在主線程和Web Worker之間傳遞數(shù)據(jù)時,會產(chǎn)生復(fù)制的開銷,并且對數(shù)據(jù)的修改在不同線程中是不共享的。
  • 兼容性:結(jié)構(gòu)化克隆在大多數(shù)現(xiàn)代瀏覽器中得到支持,但并不是所有瀏覽器都支持。一些老舊的瀏覽器可能不支持結(jié)構(gòu)化克隆或者只支持部分數(shù)據(jù)類型的結(jié)構(gòu)化克隆。

在傳輸過程中,當(dāng)使用postMessage()方法傳遞數(shù)據(jù)時,瀏覽器會自動使用結(jié)構(gòu)化克隆對數(shù)據(jù)進行序列化和反序列化的過程,以便在不同線程間傳遞數(shù)據(jù),但結(jié)構(gòu)化克隆可能會帶來性能開銷和兼容性問題,需要根據(jù)具體情況來選擇合適的解決方案。在不支持結(jié)構(gòu)化克隆的瀏覽器下,使用postMessage()傳輸數(shù)據(jù)需要使用JSON對數(shù)據(jù)內(nèi)容進行字符串轉(zhuǎn)化和解析,這也會帶來一定的性能損耗和數(shù)據(jù)類型限制。

優(yōu)化方案

  • 分割數(shù)據(jù):將大規(guī)模的數(shù)據(jù)分割成較小的塊進行傳遞,而不是一次性傳遞整個數(shù)據(jù)。例如,可以將大型數(shù)組切割成多個小塊,分別傳遞給Web Worker,然后在Web Worker中重新組合這些小塊,從而減少單次傳遞的數(shù)據(jù)量。
  • 使用共享內(nèi)存:共享內(nèi)存是一種在Web Worker和主線程之間共享數(shù)據(jù)的方式,而無需進行復(fù)制。這樣可以避免結(jié)構(gòu)化克隆的性能開銷。共享內(nèi)存可以通過使用TypedArray和ArrayBuffer來實現(xiàn),可以在主線程和Web Worker之間直接共享數(shù)據(jù)的引用,而不需要進行復(fù)制。需要注意的是,共享內(nèi)存可能需要使用鎖或其他同步機制來確保對共享數(shù)據(jù)的訪問是安全的。
  • 使用其他序列化方式:除了結(jié)構(gòu)化克隆,還可以考慮使用其他的序列化方式,例如JSON.stringify和JSON.parse。雖然JSON序列化和反序列化可能比結(jié)構(gòu)化克隆更慢,但它不會像結(jié)構(gòu)化克隆一樣復(fù)制整個數(shù)據(jù)(因僅支持部分數(shù)據(jù)類型,以及會無視undefined的字段等),而是將數(shù)據(jù)轉(zhuǎn)換為JSON字符串,并在接收方解析JSON字符串成JavaScript對象。這樣可以一定的避免復(fù)制大規(guī)模的數(shù)據(jù),從而降低性能開銷。
  • 使用壓縮算法:對于大規(guī)模的數(shù)據(jù),可以考慮使用壓縮算法對數(shù)據(jù)進行壓縮,從而減小數(shù)據(jù)的大小,降低傳輸?shù)臄?shù)據(jù)量。在接收方進行解壓縮后再進行處理。常見的壓縮算法有g(shù)zip、zlib等,可以在主線程和Web Worker之間使用這些算法對數(shù)據(jù)進行壓縮和解壓縮。

postMessage 簡單封裝

主進程封裝

// 定義一個 WorkerMessage 類,用于向 Worker 發(fā)送消息并處理返回結(jié)果
let canStructuredClone;
export class WorkerMessage {
    constructor(workerUrl) {
        this.worker = new Worker(workerUrl);
        this.callbacks = new Map();
        canStructuredClone === undefined && this.isStructuredCloneSupported();
        // 監(jiān)聽從 Worker 返回的消息
        this.worker.addEventListener('message', event => {
            const {id, type, payload} = event.data;
            const callback = this.callbacks.get(id);
            if (!callback) {
                console.warn(`未知的消息 ID:${id}`);
                return;
            }
            switch (type) {
                case 'SUCCESS':
                    callback.resolve(payload);
                    break;
                case 'ERROR':
                    callback.reject(payload);
                    break;
                default:
                    console.warn('未知的消息類型:', type);
            }
            this.callbacks.delete(id);
        });
    }
    // 發(fā)送消息給 Worker
    postMessage(payload) {
        const id = Date.now().toString(36) + Math.random().toString(36).substr(2);
        const message = canStructuredClone ? {id, payload} : JSON.stringify({id, payload})
        this.worker.postMessage(message);
        return new Promise((resolve, reject) => {
            this.callbacks.set(id, {resolve, reject});
        });
    }
    // 關(guān)閉 Worker
    terminate() {
        this.worker.terminate();
    }
    // 判斷當(dāng)前瀏覽器是否支持結(jié)構(gòu)化克隆算法
    isStructuredCloneSupported() {
        try {
            const obj = {data: 'Hello'};
            const clonedObj = window.postMessage ? window.postMessage(obj, '*') : obj;
            return canStructuredClone = clonedObj !== obj;
        } catch (error) {
            // 捕獲到異常,說明瀏覽器不支持結(jié)構(gòu)化克隆
            return canStructuredClone = false;
        }
    }
}

在上面的代碼中,我們定義了一個名為 WorkerMessage 的類,用于向 Worker 發(fā)送消息并處理返回結(jié)果。在該類的構(gòu)造函數(shù)中,我們首先創(chuàng)建了一個 Worker 實例,并監(jiān)聽了 message 事件。我們使用一個 Map 對象來保存每個消息的回調(diào)函數(shù),以便后續(xù)能夠根據(jù)消息 ID 找到對應(yīng)的回調(diào)函數(shù)。當(dāng)從 Worker 返回的消息中包含了 ID 時,我們從 Map 中找到對應(yīng)的回調(diào)函數(shù),并根據(jù)消息的類型分別調(diào)用 resolve 和 reject 方法。在調(diào)用這些方法后,我們需要從 Map 中刪除對應(yīng)的回調(diào)函數(shù),以避免內(nèi)存泄漏。

在 WorkerMessage 類中,我們定義了一個 postMessage 方法,用于向 Worker 發(fā)送消息并處理返回結(jié)果。在該方法中,我們首先生成一個唯一的消息 ID,并構(gòu)造了要發(fā)送給 Worker 的消息。然后我們使用 worker.postMessage 方法發(fā)送該消息,并返回一個 Promise 對象,以便業(yè)務(wù)層進行異步處理。在該 Promise 對象中,我們使用 callbacks.set 方法將該消息 ID 和對應(yīng)的回調(diào)函數(shù)保存到 Map 中。

在 WorkerMessage 類中,我們還定義了一個 terminate 方法,用于關(guān)閉 Worker 實例。該方法會調(diào)用 worker.terminate 方法來關(guān)閉 Worker。

同時,我們使用 isStructuredCloneSupported 方法判斷當(dāng)前瀏覽器or環(huán)境是否支持結(jié)構(gòu)化克隆,以外部 canStructuredClone 進行標記,并只在對象首次實例化的時候進行復(fù)制。如果當(dāng)前瀏覽器不支持結(jié)構(gòu)化克隆,則postMessage使用JSON.stringify轉(zhuǎn)換成字符串。

子進程封裝類const res = this.configtype);

export class childMessage {
    constructor(self, config) {
        this.self = self;
        this.config = config;
    }
    // 監(jiān)聽從主線程傳來的消息
    addEventListener(callback) {
        this.self.addEventListener('message', event => {
            const {id, payload} = event.data;
            const {type} = payload;
            try {
                const res = this.config[type](canStructuredClone ? payload.payload : JSON.parse(payload.payload));
                if (res instanceof Promise) {
                    res.then(data => {
                        this.self.postMessage({
                            id,
                            type: 'SUCCESS',
                            payload: canStructuredClone ? data : JSON.stringify(data)
                        });
                    }).catch(e => {
                        this.self.postMessage({id, type: 'ERROR', payload: e.toString()});
                    });
                } else {
                    this.self.postMessage({
                        id,
                        type: 'SUCCESS',
                        payload: canStructuredClone ? res : JSON.stringify(res)
                    });
                }
            } catch (e) {
                this.self.postMessage({id, type: 'ERROR', payload: e.toString()});
            } finally {
                callback?.();
            }
        });
    }
}

這個子進程消息傳遞的類,通過監(jiān)聽主線程發(fā)送的消息,并使用傳入的 config 對象處理不同類型的消息,主進程通過指定執(zhí)行函數(shù)的type,由worker來調(diào)用制定的函數(shù)。其中,callback 參數(shù)是一個可選的回調(diào)函數(shù),在處理完一條消息后可以執(zhí)行。其中addEventListener(callback)通過添加一個消息監(jiān)聽器,接收一個回調(diào)函數(shù)作為參數(shù)。在這個方法中,通過調(diào)用 addEventListener 方法,監(jiān)聽主線程發(fā)送過來的消息。然后對收到的消息進行處理,并將處理結(jié)果返回給主線程。如果結(jié)果是一個 Promise,則使用 then 方法處理異步結(jié)果,并將結(jié)果發(fā)送給主線程。如果結(jié)果是一個普通值,則直接將結(jié)果發(fā)送給主線程。在處理完一條消息后,會執(zhí)行可選的 callback 回調(diào)函數(shù)。

同時也使用了canStructuredClone,如果瀏覽器支持結(jié)構(gòu)化克?。╯tructured clone)算法,則直接將 payload 傳給處理函數(shù)。否則,將 payload 進行 JSON 轉(zhuǎn)換,并將其傳給處理函數(shù)。

使用案例

主進程

// 創(chuàng)建一個 WorkerMessage 實例,并指定要加載的 Worker 文件路徑
import {WorkerMessage} from "../index.js";
console.log('WorkerMessage start')
const worker = new WorkerMessage('./worker.js');
// 發(fā)送一個消息給 Worker,并處理返回結(jié)果
worker.postMessage({type: 'CALCULATE', payload: 10}).then(
    result => {
        console.log('計算結(jié)果:', result);
    },
    error => {
        console.error('計算出錯:', error);
    }
);
// 關(guān)閉 Worker 實例
// worker.terminate();
worker.postMessage({type: 'PLUS', payload: 10}).then(
    result => {
        console.log('計算結(jié)果:', result);
    },
    error => {
        console.error('計算出錯:', error);
    }
);

worker.js worker進程

import {childMessage} from "../index.js";
// 執(zhí)行計算的函數(shù)
function doCalculate(num) {
    // 這里可以執(zhí)行一些復(fù)雜的計算任務(wù)
    return num * 2;
}
function doPlus(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(num + 1);
        }, 1000);
    });
}
const child = new childMessage(self, {
    CALCULATE: doCalculate,
    PLUS: doPlus
});
// 起到分發(fā)執(zhí)行的效果
child.addEventListener(()=>{
    console.log('worker is listened');
});

輸出

image-20230420130706140

以上就是Web Woker與主線程通信場景下對postMessage的簡潔封裝詳解的詳細內(nèi)容,更多關(guān)于Web Woker封裝postMessage的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論