一周前我问了一个类似的问题,但对我的问题没有实际答案。那是因为我暴露了问题的方式。
我正在编写一个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/
答案 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 ) { });
是更优雅的解决方案之一,因为它允许它们以一种风格编写代码,该风格显示同步控制逻辑,同时仍然是异步调用。它还允许他们以任何他们喜欢的方式处理错误条件。
我知道一开始很讨厌,但这只是异步编程的现实。