Mozilla WebExtension API storage - Debugging with

2020-07-27 02:40发布

问题:


Hello guys,
i am trying to implement an add-on for the Mozilla-Firefox Browser. The following script shows one backgorund script I already successfully integrated. It uses the Mozilla WebExtension API storage. It gets executed but the log on the browser console suprises me. I get alternating logged nothing and:

bla
bla
bla

If and only if I set breakpoints on significant lines (especially the last 5 lines) of my code in Debugging-Mode I get always the expected result:

bla

How is it that the output depends on setting breakpoints. I have no idea what is happening and can not find any similar problems on the internet. Can somebody explain to me what is happening and what I can do to prevent the wrong output?

storeManager.js:

var localStore = chrome.storage.local;  
var StoreManager = function(){};

StoreManager.prototype.addItem = function(type, item){
    var thisRef = this;

    localStore.get(type, 
        function(obj){
            if(chrome.runtime.lasterror){
                console.log(chrome.runtime.lastError);
            }else{
                var oldArr = obj[type];

                if(oldArr === undefined)
                    oldArr = [];

                if(oldArr.indexOf(item) !== -1)
                    return;

                oldArr.push(item);
                thisRef.setItem(type, oldArr);
            }
        }
    );
}

StoreManager.prototype.removeItem = function(type, item){
    var thisRef = this;

    localStore.get(type, 
        function(obj){
            if(chrome.runtime.lasterror){
                console.log(chrome.runtime.lastError);
            }else{
                var oldArr = obj[type];

                if(oldArr === undefined)
                    return;

                var index = oldArr.indexOf(item);
                if(index === -1)
                    return;

                oldArr.splice(index, 1);
                thisRef.setItem(type, oldArr);
            }   
        }
    );
}

StoreManager.prototype.setItem = function(type, item){
    localStore.set({ [type] : item }, 
        function(){
            if(chrome.runtime.lasterror){
                console.log(chrome.runtime.lastError);
            }
        }
    );
}

StoreManager.prototype.visitItems = function(type, visit){
    localStore.get(type, 
        function(obj){
            if(chrome.runtime.lasterror){
                console.log(chrome.runtime.lastError);
            }else{
                var oldArr = obj[type];

                if(oldArr !== undefined){
                    for(var i=0; i<oldArr.length; i++){
                        visit(oldArr[i]);
                    }
                }
            }
        }
    );
}

function justLog(str){
    console.log(str);
}

var sm = new StoreManager();
sm.visitItems("lol", justLog);
sm.addItem("lol", "bla");
sm.visitItems("lol", justLog);
sm.removeItem("lol", "bla");
sm.visitItems("lol", justLog);


manifest.json:

{
    "manifest_version": 2,
    "name": "Addon",
    "version": "0.0",

    "description": "no desc",

    "background": {
        "scripts": [
            "storeManager.js"
        ]
    },

    "permissions": [
        "storage"
    ]
}

回答1:

storage.local.set() is asynchronous. The data isn't guaranteed to be visible to a call to get() until the callback is invoked (or if you use browser.storage.local, set() will return a Promise and the data will be guaranteed to be written when that Promise resolves).

Your code is inherently full of race conditions. The calls to addItem() and removeItem() are racing against the calls to visitItems(), by setting breakpoints you let one path or the other "win" the race.

If you expect deterministic results, I would suggest using the Promise variants of storage.local and writing your code as a series of chained handlers.