我有一个自举扩展程序,它与Firefox的chrome部分交互(即甚至在内容加载之前),并且需要query an SQLite database进行检查。我希望进行同步通话。但是,由于同步调用在性能方面很差,并且可能导致可能的UI问题,因此我需要进行异步DB调用。
我的用例是这样的:
现在,可以通过在handleCompletion
功能的executeAsync
部分中放置“进一步处理”部分来轻松处理。
但是,无论该语句是否执行,我都希望完成“进一步处理”,即该数据库查找可能会或可能不会发生。如果一切都不好,那就继续吧。如果是这样,我需要等待。因此,我正在使用基于标志的策略;我在handleCompletionCalled
和handleError
回调中将标志handleCompletion
设置为true
。
在进一步处理中,我做一个
while(handleCompletionCalled) {
// do nothing
}
//further processing
这是一个好策略还是我可以做得更好(我真的不想使用观察者,等等,因为我在整个扩展中都有很多这样的情况,并且我的代码将充满观察者)?
使用while
循环等待是严重的Bad Idea™。如果这样做,结果将是您挂起了UI,或者至少通过快速尽可能快地循环遍历循环来至少驱动CPU使用率。1
关于异步编程的要点是,您先启动一个动作,然后在活动完成或失败后执行另一个函数(回调)。这使您可以启动多个操作,或将处理放弃到整个代码的其他部分。通常,此回调应处理所有依赖于异步操作完成的活动。回调函数本身不必包含执行其他处理的代码。完成响应异步操作完成所需的操作后,它可以调用另一个函数,例如doOtherProcessing()
。
如果启动多个异步操作,则可以通过为每个任务设置标志并在所有不同的回调函数的末尾调用单个函数来等待所有异步操作的完成,例如:
function continueAfterAllDone(){
if(task1Done && task2Done && task3Done && task4Done) {
//do more processing
}else{
//Not done with everything, yet.
return;
}
}
可以通过使用数组或任务队列将其扩展到任意数量的任务,然后函数可以检查它们是否全部完成,而不是一组硬编码的任务。
等待中:如果要执行另一个处理路径,但是必须等待异步操作完成,则应该通过设置计时器或间隔来执行等待。然后,您在指定的时间段内让处理器工作,直到再次检查以查看是否发生需要进行的条件。
在自举程序附件中,您可能需要使用nsITimer
接口来实现超时或间隔计时器。这是必需的,因为在您运行初始化代码时,可能没有nsITimer
存在(即,不可能访问<window>
)。
如果要执行等待其他任务的操作,则可以执行以下操作:
window.setTimeout()
Firefox启动时的初始化代码:上面的代码是根据我用来延迟一些启动操作的操作而修改的,这些操作在显示Firefox UI之前没有完成[]。在我的一个附加组件中,我有合理的处理量,但对于要向用户显示Firefox UI而言,这不是 绝对必要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();
}
”。]因此,为了不延迟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); }
中完成的操作不一定包含用户首次激活附加组件之前所需的所有内容。就我而言,这是在用户按下附加组件的UI按钮或通过上下文菜单调用它之前必须完成的所有事情。超时可能/应该更长(例如10秒),但为5秒,因此我在开发过程中不必等待这么长时间来进行测试。请注意,只有在用户按下加载项的UI按钮之后,才能/应该完成一次/启动任务。1。这是一个普遍的编程问题:在某些编程语言中,如果从不从主代码中产生处理器,则可能永远不会调用回调。在这种情况下,您将仅锁定
startupLater()
循环而永不退出。