【问题标题】:Pass a nested array in deferred.done() callback to the outer scope将 deferred.done() 回调中的嵌套数组传递给外部作用域
【发布时间】:2013-02-06 02:50:32
【问题描述】:

一周前我问了一个类似的问题,但我的问题没有真正的答案。那是因为我暴露问题的方式。

我正在编写一个 jquery 谷歌地图插件,所以我们可以这样做:

var my_map = $('#map').plugin();
my_map.addMarker( 'Melbourne, Australia' );
my_map.addMarker( 'New York, USA' );
console.log ( my_map.getMarkers() );  // how to return markers array here ?

为了添加我的标记,我使用了谷歌地理编码,它将地址转换为纬度和经度。一旦我得到响应,我就会将我的标记添加到地图中并将它们存储在一个数组中。

一切正常。 为了检索我的标记数组,我在插件中完成了这个:

self.getMarkers = function()
{
    self._deferred.done( function()
    {
        // I need to pass this to the outer scope
        console.log( 'Deferred done :' , self.markers ); 
        return self.markers;
    });
 }

如果我只返回 self.markers 而不使用 deferred 则数组为空,因为响应尚未返回,但如果我使用 deferred 则无法将数组传递到外部作用域。

另外,我不希望使用插件的开发人员必须这样做:

my_map.done( function( markers ) {
   // code
});

我可以通过返回承诺来做到这一点,但我想通过 my_map.getMarkers() 获得标记数组

我希望问题很清楚,如果不是,请向我寻求说明。 如果您需要查看完整代码,请点击此处:http://jsfiddle.net/Vy4da/

【问题讨论】:

  • 为了让my_map.done 传递标记数组而不是单独的addMarker 会好得多,这样您就可以跟踪数组的状态并在内部触发回调
  • @charlietfl 这实际上是原始版本中的内容我将标记对象传递给addMarkers(),但我简化了插件以暴露我的实际问题,但感谢您的建议:)
  • OK..但是如果您使用方法addMarkers,而不是将数组传递给初始化对象,则在初始化时无法知道是否传入了标记
  • 建议看看 gmap3。是谷歌地图的一个很棒的包装器
  • @charlietfl 我知道gmap3,我做这个插件主要是为了学习。

标签: jquery callback jquery-deferred


【解决方案1】:

这不是异步代码的工作方式。当您调用 my_map.getMarkers() 时,如果它进行异步调用,您绝对不能保证它何时完成并且可以在调用“之后”访问代码。你必须揭露这是一个异步调用的事实不知何故

在我看来,暴露最少的方式是允许用户提供一个回调函数,该函数使用以下方式返回的数组:

my_map.getMarkers(function (array) {
    //do stuff with returned array here
});

您可以将该回调函数绑定到.done。不过,我认为通过返回承诺本身,您将获得更大的灵活性。我想你可以两者兼得。

【讨论】:

  • 感谢您的回答。 getMarkers() 本身不进行异步调用,但它需要等待 addMarker()(进行异步调用的那个)完成,所以我想这是同一回事。我认为这是你在这里给我的一个很好的解决方案,我只是想知道我是否可以有一种外观,所以调用getMarkers() 将在标记准备好时返回它们。但我猜不是。
  • @jerome.s 您将如何处理getMarkers 未准备好的情况?你无法知道那是什么时候
  • 是的,没有办法知道,我想可能有一个,这就是我来这里问这个问题的原因。谢谢:)
【解决方案2】:

根据您的评论,您至少还有 2 个选项。

由于getMarkers() 确实是一个同步调用,它对已经发生的事情进行数据处理,您只需要处理addMarker() 异步的情况。在这里,您需要做的就是确保您已完成添加标记。例如:

// For this to work, you need to put return statements at the start of addMarker and
// placeMarker so that they return the promises they are making

$.when(my_map.addMarker("Melbourne"), my_map.addMarker("New York, USA"))
.then(function()
{
   // a synchronous my_map.getMarkers() is safe to use here
   // just return self._Markers.markers;
});

如您所见,唯一的困难是并行化 addMarker 事件,并且只有在它们全部完成后才使用 getMarkers。我建议将这部分作为插件的一部分,以方便您的开发人员。

self.addMarkers = function( /*...*/ ) // accept array of location or comma separated list
{
    var args = [].concat(Array.prototype.slice.call(arguments));
    return $.when.apply(null, args.map(self.addMarker, self));
}

用法:

my_map.addMarkers('Melbourne, Australia', 'New York, USA')
.then(function()
{
   console.log(my_map.getMarkers()); // works if synchronous
   console.log(my_map._Markers.markers); // also works
});

查看实际操作:http://jsfiddle.net/Vy4da/1/

您的第二个选择是在向您的my_map 发出请求后,返回一个与原始请求对象 分开的评估对象。这是 AJAX 在 Jquery 中使用的模式。您将选项传递给$.ajax,然后将其与done(function(data,status,jqXHR){...}) 链接起来,其中jqXHR 是一个评估对象,可以让您评估结果,data 是您可能最感兴趣的部分(jqXHR.responseText )。要在您的情况下使用相同的模式,getMarkers() 根本不会出现在 my_map 上,而是附加到由映射请求函数解析的构造对象。这种方法可以防止开发人员在 getMarkers() 可用之前错误地使用它,因为它根本没有被定义,除非通过 Promise 解决。


原答案 (基于 getMarkers 是异步的误解)

如果my_map.getMarkers() 是任何类型的异步请求,则不能将其视为同步变量赋值。

所以:var a = my_map.getMarkers()永远在其中包含异步调用的结果。

如果您改为返回 Promise,则您授予调用者选项来决定如何将此调用与其他异步调用并行化。

my_map.getMarkers().done( function( markers ) { }); 是解决这个问题的更优雅的解决方案之一,因为它允许他们以一种显示类似同步的控制逻辑的风格编写代码,同时仍然是一个异步调用。它还允许他们以他们喜欢的任何方式处理错误情况。

一开始我知道这很烦人,但这只是异步编程的现实。

【讨论】:

  • 如果您查看完整代码,您会发现getMarkers() 不是异步调用。它旨在检索标记数组。一旦标记被放置在地图上,标记数组就会存储它们。不过,你说的都是真的。我在我的问题中提到了返回一个承诺,但我没有看到它的价值。现在,按照您解释的方式,我认为返回一个承诺将是最好的选择,因为这将为使用该插件的人提供灵活性。
  • @jerome.s 啊!有了它,你有更多的选择。更新
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
  • 2018-02-19
  • 2013-10-17
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多