在Firefox Restartless Extension中使用while循环是一个好的等待策略吗?



我有一个bootstrap扩展,它与Firefox的chrome部分交互(即甚至在内容加载之前),并且需要查询SQLite数据库进行一些检查。我更喜欢同步呼叫。但是,由于同步调用在性能方面很糟糕,并且可能导致UI问题,因此我需要进行异步DB调用。

我的用例如下:

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

现在,这可以通过在executeAsync函数的handleCompletion部分中放置"进一步处理"部分来轻松处理。

但是,我希望"进一步的处理"能够完成,而不管这个语句正在执行,即这个DB查找可能会发生,也可能不会发生。如果发生得不好,那就继续。如果有的话,我需要等待。所以,我使用的是基于旗帜的策略;我在handleError中设置了一个标志handleCompletionCalledhandleCompletion回调true .

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

while(handleCompletionCalled) {
 // do nothing
}
//further processing

这是一个好策略,或者我可以做一些更好的(我真的不想使用观察者等,因为我有很多这样的情况在我的整个扩展和我的代码将充满观察者)?

使用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显示给用户。[参见"扩展中的性能最佳实践"。因此,为了不延迟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循环中,并且永远不会退出。

相关内容

  • 没有找到相关文章

最新更新