【问题标题】:How to avoid this async lazy pattern?如何避免这种异步惰性模式?
【发布时间】:2015-01-28 14:34:31
【问题描述】:

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

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

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

有没有一种模式可以避免在这段代码中写两次以下行?

doSomeTreatment(myvar)

【问题讨论】:

  • 哎呀——这些是 jQuery 承诺还是新的“标准”承诺?
  • 我的问题是不可知的,以便答案可以提出最佳解决方案!因此,只需使用最佳标准或不使用 Promise、Deferred 或其他任何您喜欢的标准。

标签: javascript asynchronous promise


【解决方案1】:

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

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

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

如果需要,在 doSomeTreatment() 内部而不是在匿名 .then 回调内部分配 myVar,这允许您将 doSomeTreatment 作为函数引用直接传递。

EDIT 哎呀,你想要标准的 Promise 还是 jQuery 的 Promise?以上是jQuery风格。

【讨论】:

  • jQuery 的承诺让我哭了。团队怎么可能get them so very wrong
  • @Sukima 因为他们在我相信 A+ 之前就已经标准化了 API。
  • @GeorgeMauer 我不确定以前是这样,但我相信 jQuery 采用 Promise-like API 对推广这一概念大有帮助。跨度>
【解决方案2】:

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

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

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

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

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

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

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

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

是否要将参数传递给getData

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

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

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

【讨论】:

  • 我真的很惊讶你告诉我这不是一个有用的模式。这是一种非常经典的延迟加载方式,附带异步参数。恕我直言,这是在性能和​​ GUI 反应性方面非常好的优化。
  • @RémiDoolaeghe 我并不是说这不是一个有用的模式,但也许我设计 UI 架构的方式和我使用的库(我是淘汰赛和 ReactJs 的忠实粉丝)是这样做的一种使这通常变得不必要的方法。我当然不会预先加载所有数据,但我必须手动管理诸如undefined 检查之类的守卫是非常罕见的,甚至更罕见的是我会在回调中从更高范围修改变量。我认为您可以进行一些架构更改来完全避免该问题。
  • 我需要考虑一下。你说的可以说得通。我需要看看我是否可以调整遗留代码以适应这一愿景。感谢您准确地提出您的想法。
  • 我有一个例子。让我们有一个页面,上面有一个按钮。单击此按钮后,将显示一个对话框,其中包含一个可供选择的项目。在页面加载时,您不知道按钮会被单击,因此您不会预加载项目。当用户单击按钮时,您需要加载项目。但也许该按钮之前已经被点击过,这样您就不必重新加载项目(我想项目不会在两次点击之间改变)。在这种情况下,我的模式适用。
  • 谢谢@RémiDoolaeghe 是的,你会发现在 js 中以函数式风格做事通常比程序式甚至 OO 风格更容易。这是有道理的,scheme 可能是对 javascript 影响最大的语言(self 是另一个大语言,事实上它有点像 java 只是 Netscape 的营销噱头)。我强烈推荐阅读Javascript the Good PartsJavascript Allonge 以获得有效的入门。
【解决方案3】:

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

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

【讨论】:

  • 我需要重新分配 myvar,因为我使用了两种与 Ractive 绑定的方式,所以我将在异步加载函数中重新分配
  • 嗯好吧,只要确保不要依赖该值来调用loadMyVarAsynchronously(),最好直接查找promise
  • 这是有道理的。感谢您的警告。
【解决方案4】:

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

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

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

【讨论】:

  • 我有一个疑问:考虑一个已经解决的 Promise。给它一个回调(调用 then(...) )将立即调用它。
  • @RémiDoolaeghe 当.then() 被一个已经解决的promise 调用时,处理程序将在Promises/A+ 兼容的promise 上被异步调用,并立即在jQuery promises 和其他一些类型的promise 上被调用。承诺。
猜你喜欢
  • 2017-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多