在runCalculation函数中,为了等待Worker对象完成自己的工作,或者通过延时而重复相同的函数调用(即异步递归),或者简单地存储回调结果。 哪种方式更好呢?乍一看,只使用异步递归是最简单的,因为这里不再需要calculationCallbacks对象。出于这个目的,JavaScript新手常常会使用setTimeout,因为它很像线程型语言的风格。此程序的Java版本可能会有这样一个循环: while (!calculationCache.get(formula)) { Thread.sleep(0); }; 但是,延时并不是免费的午餐。大量延时的话,会造成巨大的计算荷载。异步递归有一点很可怕,即在等待任务完成期间,可触发之延时的次数是不受限的!此外,异步递归还毫无必要地复杂化了应用程序的事件结构。基于这些原因,应将异步递归视作一种“反模式”的方式。 在这个计算器例子中,为了避免异步递归,可以为每个公式存储一个回调数组。 var calculationCache = {}, calculationCallbacks = {}, mathWorker = new Worker('calculator.js'); mathWorker.addEventListener('message', function(e) { var message = e.data; calculationCache[message.formula] = message.result; calculationCallbacks[message.formula] .forEach(function(callback) { callback(message.result); }); }); function runCalculation(formula, callback) { if (formula in calculationCache) { return callback(calculationCache[formula]); }; if (formula in calculationCallbacks) { return calculationCallbacks[formula].push(callback); }; mathWorker.postMessage(formula); calculationCallbacks[formula] = [callback]; } 没有了延时,我们的代码要直观得多,也高效得多。 总的来说,请避免异步递归。仅当所采用的库提供了异步功能但没有提供任何形式的回调机制时,异步递归才有必要。如果真的遇到这种情况,要做的第一件事应该是为该库写一个补丁。或者,干脆找一个更好的库。