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

时间:2015-01-28 14:34:31

标签: javascript asynchronous promise

很多时候,我需要在Javascript中编写这样一个懒惰的异步加载:

if (myvar != undefined){
    doSomeTreatment(myvar)
} else {
    loadMyVarAsynchronously().then(function(value){
        myvar = value
        doSomeTreatment(myvar)
    })
}

这里,myvar是哈希的一些属性,而不是局部变量。 loadMyVarAsynchronously异步加载myvar的值(例如,使用PromiseJQuery Deferred

是否有一种模式可以避免在此代码中写入以下两行?

doSomeTreatment(myvar)

4 个答案:

答案 0 :(得分:2)

如果已定义myvar,您可以将其传递到$.when()并将其自动包装到已解决的承诺中:

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

$.when(def).then(doSomeTreatment);

如果需要,在myVardoSomeTreatment()进行分配,而不是在匿名.then回调内,这样您就可以直接将doSomeTreatment作为函数引用传递。

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

答案 1 :(得分:1)

是的,你应该建立一个价值承诺。然后你只需要将该函数作为处理程序附加一次:

var loadedMyVar = myvar!=undefined ? Promise.resolve(myvar) : loadMyVarAsynchronously();
loadedMyVar.then(doSomeTreatment);

哦,您不需要异步重新分配给myvar,只需在任何地方使用loadedMyVar承诺。甚至可以使用

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

答案 2 :(得分:1)

如果没有一个具体的,现实世界的例子,你很难在这里做出正确的理由。这绝对不是我在代码中使用的模式,所以我怀疑你可以通过重新评估你的假设来解决这个问题。

话虽如此,你可以使用承诺链。请注意,在这种情况下,promiseThatLoadsVar是一个承诺,它不是一个返回承诺的函数。这意味着它维持一个状态,并且第二次该代码向前运行时它将立即返回。

promisethatLoadsVar.then(function(value) {
     myvar = value;
     doSomeTreatment(myvar);
})

如果您能提出更明确的问题,我很乐意为您提供更清晰的答案。

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

var getData = _.once(function() { return $.getJSON(...) });

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

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

是否要将参数传递给getData

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

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

这将做同样的事情,但是通过传递给getData的第一个输入参数来缓存promises。

答案 3 :(得分:1)

使用myvar != undefined检查是否检索它是一个问题,那就是当你启动另一个时,可能已经有一个正在检索的检索。你最终会执行两次(或更多!)的请求。

为了避免这种情况,您可以保留承诺,将其视为myvar未来值,而不是myvar

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