将deferred.done()回调中的嵌套数组传递给外部作用域

时间:2013-02-06 02:50:33

标签: jquery callback jquery-deferred

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

我正在编写一个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/

2 个答案:

答案 0 :(得分:1)

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

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

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

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

答案 1 :(得分:1)

根据您的评论,您至少还有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 ) { });是更优雅的解决方案之一,因为它允许它们以一种风格编写代码,该风格显示同步控制逻辑,同时仍然是异步调用。它还允许他们以任何他们喜欢的方式处理错误条件。

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