Firefox无重启扩展 - 使用while循环是一个很好的等待策略吗?

时间:2016-08-09 08:31:51

标签: sqlite firefox-addon firefox-addon-restartless firefox-addon-bootstrap

我有一个自举扩展程序,它与Firefox的chrome部分交互(即使在内容加载之前),并且需要query an SQLite database进行一些检查。我更喜欢同步通话。但是,由于同步调用在性能方面不好并且可能导致UI问题,我需要进行异步数据库调用。

我的用例是这样的:

  • 对数据库进行aysnc调用
  • 完成后再进行处理

现在,通过将{进一步处理'部分放在handleCompletion函数的executeAsync部分中,可以轻松处理此问题。

但是,我想要进行'进一步处理'而不管这个语句是否被执行,即这个DB查找可能会也可能不会发生。如果它没有好好发生,请继续。如果确实如此,我需要等待。 所以,我使用的是基于旗帜的策略;我在handleCompletionCalled&中设置了一个标记handleError handleCompletion回调true

在进一步处理部分,我做了

while(handleCompletionCalled) {
 // do nothing
}

//further processing

这是一个好策略还是我可以做得更好(我真的不想使用Observers等,因为我在我的整个扩展中有很多这样的情况,我的代码将填充Observers)?< / p>

1 个答案:

答案 0 :(得分:2)

使用while循环等待是一个严重错误的想法™。如果这样做,结果将是您挂起UI,或者至少通过尽可能快地快速运行循环来驱动CPU使用率。 1

关于异步编程的观点是,您启动一​​个操作,然后在活动完成后执行另一个函数(回调),或者失败。这允许您启动多个操作,或者将处理放弃到整个代码的其他部分。通常,此回调应处理依赖于异步操作完成的所有活动。回调函数本身不必包含执行其他处理的代码。在完成异步操作完成后需要完成的操作之后,它可以调用另一个函数,如doOtherProcessing()

如果启动多个异步操作,则可以通过为每个任务设置标志以及在所有不同回调函数结束时调用的单个函数等待所有这些操作的完成等等:

function continueAfterAllDone(){
    if(task1Done && task2Done && task3Done && task4Done) {
        //do more processing
    }else{
        //Not done with everything, yet.
        return;
    }
}

这可以通过使用数组或任务队列扩展到任意数量的任务,然后该函数检查所有这些任务是否完成而不是硬编码的任务集。

<强>等待:
如果您要执行另一个处理路径,但必须等待异步操作的完成,则应通过设置计时器或间隔来执行等待。然后,您将处理器停留一段指定的时间,直到再次检查以确定是否需要继续处理这些条件。

在引导加载项中,您可能需要使用nsITimer接口来实现超时或间隔计时器。这是必需的,因为在您运行初始化代码时,可能不存在<window>(即可能无法访问window.setTimeout())。

如果您打算等待其他任务,您可以这样做:

const Cc = Components.classes;
const Ci = Components.interfaces;

var asyncTaskIsDone = false;
var otherProcessingDone = false;
// Define the timer here in case we want to cancel it somewhere else.
var taskTimeoutTimer;

function doStuffSpecificToResultsOfAsyncAction(){
    //Do the other things specific to the Async action callback.
    asyncTaskIsDone = true;
    //Can either call doStuffAfterOtherTaskCompletesOrInterval() here, 
    //  or wait for the timer to fire.
    doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval();
}

function doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval(){
    if(asyncTaskIsDone && otherProcessingDone){
        if(typeof taskTimeoutTimer.cancel === "function") {
            taskTimeoutTimer.cancel();
        }
        //The task is done
    }else{
        //Tasks not done.
        if(taskTimeoutTimer){
            //The timer expired. Choose to either continue without one of the tasks
            //  being done, or set the timer again.
        }
        //}else{ //Use else if you don't want to keep waiting.
        taskTimeoutTimer = setTimer(doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval
                                    ,5000,false)
        //}
    }
}

function setTimer(callback,delay,isInterval){
    //Set up the timeout (.TYPE_ONE_SHOT) or interval (.TYPE_REPEATING_SLACK).
    let type = Ci.nsITimer.TYPE_ONE_SHOT
    if(isInterval){
        type = Ci.nsITimer.TYPE_REPEATING_SLACK
    }
    let timerCallback = {
        notify: function notify() { 
            callback();
        }
    }
    var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    timer.initWithCallback(timerCallback,delay,type);
    return timer;
}

function main(){
   //Launch whatever the asynchronous action is that you are doing.
   //The callback for that action is doStuffSpecificToResultsOfAsyncAction().

    //Do 'other processing' which can be done without results from async task here.

    otherProcessingDone = true;
    doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval();
}

Firefox启动时的初始化代码:
上面的代码是根据我用来延迟一些启动操作来修改的,这些操作在显示Firefox UI之前不会 完成。

在我的一个附加组件中,我有一个合理的处理量,但是绝对必要,以便向用户显示Firefox UI。 [请参阅“Performance best practices in extensions”。]因此,为了不延迟UI,我使用了一个计时器和一个在Firefox启动后5秒执行的回调。这允许Firefox UI对用户更敏感。代码是:

const Cc = Components.classes;
const Ci = Components.interfaces;

// Define the timer here in case we want to cancel it somewhere else.
var startupLaterTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

function startupLater(){
  //Tasks that should be done at startup, but which do not _NEED_ to be
  //  done prior to the Firefox UI being shown to the user.
}

function mainStartup(){
   let timerCallback = {
        notify: function notify() { 
            startupLater();
        }
    }
    startupLaterTimer = startupLaterTimer.initWithCallback(timerCallback,5000
                                                           ,Ci.nsITimer.TYPE_ONE_SHOT);
}

请注意,startupLater()中所做的事情并不一定包含用户首次激活广告之前所需的所有内容。在我的例子中,它是在用户按下加载项的UI按钮或通过上下文菜单调用它之前必须完成的所有事情。超时可能/应该更长(例如10秒),但是是5秒,所以我不必在开发过程中等待这么长时间进行测试。请注意,只有在用户按下加载项的UI按钮后才能/应该执行一次性/启动任务。

1。这里有一个常见的编程问题:在某些编程语言中,如果您从不从主代码中获取处理器,则可能永远不会调用您的回调。在这种情况下,您只需锁定while循环并永不退出。