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

How to avoid this async lazy pattern?

很多时候,我需要在Javascript中写这样一个延迟异步加载:

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

在这里,myvar 是散列的某个属性,而不是局部变量。 loadMyVarAsynchronously 异步加载 myvar 的值(例如,PromiseJQuery Deferred

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

doSomeTreatment(myvar)

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

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

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

如果需要,在 doSomeTreatment() 内部对 myVar 赋值,而不是在匿名 .then 回调内部,这允许您直接将 doSomeTreatment 作为函数传递参考。

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

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

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);

如果没有具体的真实示例,真的很难推断您在这里所做的事情。这绝对不是我在代码中经常使用的模式,因此我怀疑您可以通过重新评估您的假设来解决它。

也就是说,您可以使用承诺链。请注意,在这种情况下,promiseThatLoadsVar 是一个承诺,它不是 return 一个承诺的函数。这意味着它保持一个状态,并且这段代码第二次继续运行时它将立即return。

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

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

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

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

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

请注意,getData 是一个包装函数,在第一次调用后将只是 return 一遍又一遍地执行相同的操作,而无需重新调用该函数。第一次调用它时,它将 return 一个在检索数据时解析的承诺。第二次之后,您会得到 相同的承诺 ,它可能已经得到解决。

是否要给getData传入参数?

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

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

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

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

为避免这种情况,您可以坚持承诺,将其视为 myvar 未来价值 ,并且根本不使用 myvar

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