簡體   English   中英

在Chrome擴展程序中,如何使用chrome-promise確保先前的承諾在下一個承諾之前得到解決?

[英]In a Chrome extension, how to ensure previous promise resolves before the next one using chrome-promise?

我一直在使用chrome-promise庫將Chrome擴展程序API封裝在具有返回promise的外觀上,而不是使用回調。 通常,此方法效果很好,但是我似乎遇到了chrome.storage.local API的問題。

我的擴展程序的事件頁面監聽chrome.tabs.onActivatedchrome.tabs.onRemoved事件。 當它收到onActivated事件時,它將選項卡信息添加到數組中,並調用chrome.storage.local.set(data)將更新后的數組存儲在本地存儲中。

當它收到onRemoved事件時,它會調用chromepromise.storage.local.get(null).then(...)以通過promise獲取選項卡列表,從數組中刪除選項卡信息,然后調用chrome.storage.local.set()再次使用chrome.storage.local.set()保存更新后的數組。

問題是onActivated事件似乎在onRemoved事件的承諾流解析之前觸發。 因此, onActivated處理程序將檢索舊的存儲數組,其中的關閉標簽仍在其中,然后推入新激活的標簽。 因此,存儲的選項卡數據現在包括一個已經關閉的選項卡。

我假設這是使用promise而不是回調的問題,但是我想知道是否還有其他人遇到了此庫並解決了該問題。

更新資料

正如wOxxOm所指出的那樣 ,這是一個普遍的問題,即“仲裁對chrome.storage類的單個資源的不可預測的異步訪問”,而不是chrome-promise庫所獨有。

經過研究后,我提出了幾個解決方案,並在下面添加了答案。 一個使用互斥鎖來確保(我認為)一個promise鏈在chrome.storage獲取和設置數據的操作在下一個開始之前完成。 另一個則將根據事件創建的整個諾言鏈排隊,直到當前的一個完全完成后才開始下一個。 我不確定哪個更好,盡管我認為鎖定較短的時間會更好。

歡迎任何建議或更好的答案。

互斥體

更新:我最終使用下面的方法來創建一個使用互斥量的模塊 ,以確保獲取和設置Chrome擴展存儲的順序。 到目前為止,它似乎運行良好。


該解決方案采用從互斥執行這篇文章 addTab()removeTab()使用執行所有存儲獲取和設置的功能來調用storageMutex.synchronize() 這應防止以后的事件影響以前的事件的存儲。

下面的代碼是擴展的非常簡化的版本,但它確實可以運行。 底部的playNextEvent()調用會模擬打開4個選項卡,切換回選項卡2並關閉它,然后使選項卡3激活。 setTimeout()以便所有內容都不會作為一個長調用堆棧運行。

 function Mutex() { this._busy = false; this._queue = []; } Object.assign(Mutex.prototype, { synchronize: function(task) { var self = this; return new Promise(function(resolve, reject) { self._queue.push([task, resolve, reject]); if (!self._busy) { self._dequeue(); } }); }, _dequeue: function() { var next = this._queue.shift(); if (next) { this._busy = true; this._execute(next); } else { this._busy = false; } }, _execute: function(record) { var task = record[0], resolve = record[1], reject = record[2], self = this; task().then(resolve, reject).then(function() { self._dequeue(); }); } }); const storageMutex = new Mutex(); function onActivated(tabID) { console.log("EVENT onActivated", tabID); return Promise.resolve(tabID).then(tab => addTab(tab)); } function onRemoved(tabID) { console.log("EVENT onRemoved", tabID); return removeTab(tabID); } var localData = { tabs: [] }; function delay(time) { return new Promise(resolve => setTimeout(resolve, time)); } function getData() { return delay(0).then(() => JSON.parse(JSON.stringify(localData))); } function saveData(data, source) { return delay(0) .then(() => { localData = data; console.log("save from:", source, "localData:", localData); return Promise.resolve(localData); }); } function addTab(tabID) { return storageMutex.synchronize(() => getData().then((data) => { console.log("addTab", tabID, "data:", data); data.tabs = data.tabs.filter(tab => tab != tabID); data.tabs.push(tabID); return saveData(data, "addTab"); })); } function removeTab(tabID) { return storageMutex.synchronize(() => getData().then((data) => { console.log("removeTab", tabID, "data:", data); data.tabs = data.tabs.filter(tab => tab != tabID); return saveData(data, "removeTab"); })); } const events = [ () => onActivated(1), () => onActivated(2), () => onActivated(3), () => onActivated(4), () => onActivated(2), () => { onRemoved(2); onActivated(3) } ]; function playNextEvent() { var event = events.shift(); if (event) { delay(0).then(() => { event(); delay(0).then(playNextEvent) }); } } playNextEvent(); 

隊列

此解決方案使用非常簡單的排隊機制。 事件處理程序使用啟動承諾鏈來處理該事件的函數來調用queue() 如果隊列中沒有promise,則立即調用該函數。 否則,它將被推送到隊列中,並在當前的承諾鏈完成時被觸發。 這意味着一次只能處理一個事件,而效率可能不高。

 var taskQueue = []; function queue( fn) { taskQueue.push(fn); processQueue(); } function processQueue() { const nextTask = taskQueue[0]; if (nextTask && !(nextTask instanceof Promise)) { taskQueue[0] = nextTask() .then((result) => { console.log("RESULT", result); taskQueue.shift(); processQueue(); }); } } function onActivated(tabID) { console.log("EVENT onActivated", tabID); queue(() => Promise.resolve(tabID).then(tab => addTab(tab))); } function onRemoved(tabID) { console.log("EVENT onRemoved", tabID); queue(() => removeTab(tabID)); } var localData = { tabs: [] }; function delay(time) { return new Promise(resolve => setTimeout(resolve, time)); } function getData() { return delay(0).then(() => JSON.parse(JSON.stringify(localData))); } function saveData(data, source) { return delay(0) .then(() => { localData = data; console.log("save from:", source, "localData:", localData); return Promise.resolve(localData); }); } function addTab(tabID) { return getData().then((data) => { console.log("addTab", tabID, "data:", data); data.tabs = data.tabs.filter(tab => tab != tabID); data.tabs.push(tabID); return saveData(data, "addTab"); }); } function removeTab(tabID) { return getData().then((data) => { console.log("removeTab", tabID, "data:", data); data.tabs = data.tabs.filter(tab => tab != tabID); return saveData(data, "removeTab"); }); } const events = [ () => onActivated(1), () => onActivated(2), () => onActivated(3), () => onActivated(4), () => onActivated(2), () => { onRemoved(2); onActivated(3) } ]; function playNextEvent() { var event = events.shift(); if (event) { delay(0).then(() => { event(); delay(0).then(playNextEvent) }); } } playNextEvent(); 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM