Javascript 如何避免这种异步惰性模式?

Javascript 如何避免这种异步惰性模式?,javascript,asynchronous,promise,Javascript,Asynchronous,Promise,很多时候,我需要用Javascript编写这样一个惰性异步加载: if (myvar != undefined){ doSomeTreatment(myvar) } else { loadMyVarAsynchronously().then(function(value){ myvar = value doSomeTreatment(myvar) }) } 这里,myvar是散列的某个属性,而不是局部变量loadMyVarAsynchro

很多时候,我需要用Javascript编写这样一个惰性异步加载:

if (myvar != undefined){
    doSomeTreatment(myvar)
} else {
    loadMyVarAsynchronously().then(function(value){
        myvar = value
        doSomeTreatment(myvar)
    })
}
这里,myvar是散列的某个属性,而不是局部变量
loadMyVarAsynchronously
异步加载myvar的值(例如,使用
Promise
JQuery Deferred

是否有一种模式可以避免在代码中写两次下面的行

doSomeTreatment(myvar)

如果已经定义了
myvar
,则可以将其传递到
$。when()
中,并将其自动包装到已解析的承诺中:

var def = (myVar !== undefined) ? myVar : loadMyVarAsynchronously();

$.when(def).then(doSomeTreatment);
如果需要,在
DoSometreament()
内部,而不是在匿名
内部,对
myVar
进行赋值。然后
回调,这允许您将
Dometreament
作为函数引用直接传递


编辑oops,您想要标准承诺还是jQuery承诺?以上是jQuery风格。

是的,您应该为价值做出承诺。然后,只需将该函数作为处理程序附加一次:

var loadedMyVar = myvar!=undefined ? Promise.resolve(myvar) : loadMyVarAsynchronously();
loadedMyVar.then(doSomeTreatment);
哦,您不需要异步地重新分配到
myvar
,只需在任何地方使用
loadedMyVar
promise即可。甚至可以使用

var loadedMyVar = loadedMyVar || loadMyVarAsynchronously();
loadedMyVar.then(function(value) {
    return myvar = value;
}).then(doSomeTreatment);

如果没有一个具体的、真实的例子,你真的很难解释你在这里做什么。这绝对不是我在代码中经常使用的模式,因此我怀疑您可以通过重新评估您的假设来修复它

也就是说,你可以使用承诺链。注意,在本例中,PromiseThaveLoadsVar是一个承诺,它不是一个返回承诺的函数。这意味着它将保持一个状态,当代码再次运行时,它将立即返回

promisethatLoadsVar.then(function(value) {
     myvar = value;
     doSomeTreatment(myvar);
})
如果你能提出一个更清楚的问题,我很乐意为你提供一个更清楚的答案

编辑:我想详细说明评论中提出的示例。下面是一个使用库的解决方案。(顺便说一句,您应该使用lodash或下划线,它们基本上是javascript的标准库)

这里请注意,
getData
是一个包装函数,在第一次调用之后,它将反复返回相同的内容,而不重新调用该函数。第一次调用它时,它将返回一个在检索数据时解析的承诺。第二次以后,你会得到同样的承诺,这很可能已经得到解决

是否要将参数传递到
getData

var getData = _.memoize(function(id) { return $.getJSON(url+id) });

$('button').on('click', function() { 
    getData($('#selector').val()).then(function(data) { showDialog(data) }) 
})

这将做同样的事情,但是通过传递给
getData
的第一个输入参数缓存承诺使用
myvar!=未定义
作为是否检索的检查,也就是说,当您启动另一个检索时,可能已经有一个正在进行的检索。您将执行两次(或更多!)请求

为了避免这种情况,您可以坚持承诺,将其视为
myvar
的未来值,而不使用
myvar

// assume myvarpromise is persisted somewhere longer than the 
// life of this function
myvarpromise = myvarpromise || loadMyVarAsynchronously();
myvarpromise.then(doSomeTreatment);

面向对象-这些是jQuery承诺还是新的“标准”承诺?我的问题是不可知的,这样答案就可以提出最佳解决方案!所以,只要使用最佳标准,或者不承诺,或者延迟,或者任何你喜欢的东西。在我的情况下,我需要重新分配myvar,因为我使用两种方式绑定Ractive,所以我将在异步加载函数中重新分配。好的,只是确保不依赖该值来调用
loadMyVarAsynchronously()
,最好直接寻找有意义的承诺人。谢谢你的警告。我真的很惊讶你告诉我这不是一个有用的模式。这是一种非常经典的延迟加载方式,还有一个额外的异步参数。就性能和GUI反应性而言,这是一个非常好的优化。@Rémidoolage我不是说这不是一个有用的模式,但也许我设计UI架构和使用的库(我非常喜欢knockout和ReactJs)的方式会使这通常变得不必要。当然,我不会预先加载所有数据,但我必须手动管理诸如
undefined
检查之类的防护措施的情况非常罕见,更罕见的是,我会在回调中从更高的范围修改变量。我想你可以做一些架构上的改变来完全避免这个问题。我需要考虑一下。你说的有道理。我需要看看我是否可以调整遗留代码以适应这个愿景。谢谢你的建议,我举个例子。让我们有一个页面,上面有一个按钮。单击此按钮后,将显示一个对话框,其中有一个项目可供选择。在加载页面时,您不知道会单击按钮,因此您不会预加载项目。当用户单击按钮时,您需要加载项目。但是可能按钮之前已经被点击过,这样你就不必重新加载项目(我想两次点击之间项目不会改变)。在这种情况下,我的模式适用。谢谢@Rémidoolage是的,你会发现在js中以函数式风格做事通常比过程式的,甚至OO式的方式容易得多。这是有道理的,scheme可能是对javascript影响最大的语言(self是另一个重要的语言,事实上java看起来只是Netscape的一个营销噱头)。我强烈建议大家阅读,作为一本有效的入门书,jQuery的承诺让我哭泣。这个团队是怎么做到的?@苏基马,因为他们在A+之前把API标准化了。我不确定这是不是以前,但是我相信JQuery采用类似API的承诺有助于推广这个概念。我对此有疑问:考虑一个已经解决的承诺。给它一个回调(调用then(…)将立即调用它。@Rémidoolage当对已解析的承诺调用
.then()
时,将对其异步调用处理程序
// assume myvarpromise is persisted somewhere longer than the 
// life of this function
myvarpromise = myvarpromise || loadMyVarAsynchronously();
myvarpromise.then(doSomeTreatment);