Javascript 如何强制立即执行延迟的方法?
我有一个web映射应用程序,它有一个按钮,单击该按钮时调用一个返回一个大JSON的web服务。JSON字符串的一部分表示我需要将地图缩放到的空间范围。JSON的主要部分(下面的“结果”部分)需要几秒钟的处理时间,与“extentToZoomTo”部分无关。我使用的映射API有一个setExtent方法,它返回一个DOJO-Deferred对象。setExtent方法在服务器上生成一个映像,可能需要几秒钟才能完成。我想做的是在获得JSON后立即调用setExtent方法,当服务器忙于处理该方法时,浏览器上的客户端javaScript代码可以同时处理JSON的“结果”部分。我怎样才能做到这一点??我发现,如果我在开始处理“results”部分之前简单地调用setExtent,它实际上不会将该请求发送到服务器,直到results部分完全处理完毕。我可以通过检查开发人员工具中的Network选项卡来验证这种行为(在获取JSON和调用setExtent方法之间有几秒钟的间隔)。我猜这与setExtent被延迟的事实有关。我怎样才能让它马上开火Javascript 如何强制立即执行延迟的方法?,javascript,performance,asynchronous,dojo,deferred,Javascript,Performance,Asynchronous,Dojo,Deferred,我有一个web映射应用程序,它有一个按钮,单击该按钮时调用一个返回一个大JSON的web服务。JSON字符串的一部分表示我需要将地图缩放到的空间范围。JSON的主要部分(下面的“结果”部分)需要几秒钟的处理时间,与“extentToZoomTo”部分无关。我使用的映射API有一个setExtent方法,它返回一个DOJO-Deferred对象。setExtent方法在服务器上生成一个映像,可能需要几秒钟才能完成。我想做的是在获得JSON后立即调用setExtent方法,当服务器忙于处理该方法时,
{
"extentToZoomTo": {
"xmin": 1234,
"ymin": 4567,
"xmax": 2345,
"ymax": 5678
},
"results": {
//A very large amount of data here that takes several seconds to process
}
}
----编辑---
我可以并行运行两个延迟的吗?下面是伪类型脚本代码
mainMethod(){
let bigJSONString: string = "big json string with results and extent info retrieved from web service call";
//get extent info from bigJSONString...for now, just mock the values
let xmin = 123; let ymin = 234; let xmax = 345; let ymax = 789;
let jsonExtent: esri.geometry.Extent = new esri.geometry.Extent(xmin, ymin, xmax, ymax, spatialRef);
//I want to call setExtent and handResults in parallel...how do I do that?
let setExtentDeferred: dojo.Deferred = myMap.setExtent(jsonExtent);
let handleResultsDeferred: dojo.Deferred = this.handleResultsDeferred(bigJSONString);
dojo.Deferred.RUN_IN_PARALLEL(setExtentDeferred, handleResultsDeferred);
}
handleResultsDeferred(jsonString: string): dojo.Deferred {
//This code does the real work...should I return a dojo.Deferred from here?
return new dojo.Deferred();
}
延迟对象类似于标准JavaScript承诺或Java未来。这只是在后台进程完成时通知代码的一种方式。这与其他一些语言(如Go)中使用“defer”一词的方式不同,在Go中,它表示一个操作被推迟到以后 但是,
setExtent
方法仍然可以推迟到handleResults代码之后。如果发出JSON请求的setExtent
中的代码以setTimeout
或其他异步方式运行,它将被延迟到JavaScript事件循环的下一个勾号,该勾号将位于其他结果处理代码之后(假设它是同步的)
setExtent
代码实际上有两个级别的异步。首先,函数代码是异步的(这就是为什么在您期望发出请求之前,您看不到该请求被发出的原因)。第二个是请求本身。它看起来像:
let jsonExtent = expo.Extent
+---let setExtentDeferred = myMap.setExtent(jsonExtent);
| handleResults()
| // any other sync code runs
|
| -- current event loop cycle ends --
|
+-> // setExtent code actually runs here and starts a request
// other sync code runs
-- time passes --
// request completes
let jsonExtent = expo.Extent
+-----let setExtentDeferred = myMap.setExtent(jsonExtent);
| +---let handleResultsDeferred = handleResults()
| | // any other sync code runs
| |
| | -- current event loop cycle ends --
| |
+-|-> // setExtent code actually runs, starting a request
+-> // handle results code runs
-- time passes --
// request completes
// DeferredList resolves
同步操作按顺序运行,任何异步操作都将延迟到稍后的时间
为了使某些事情并行运行,您需要在发出请求之后但在请求完成之前启动处理代码。请注意,请求是其中唯一可以与其他任何内容并行运行的部分。除了像服务工作者这样的新特性之外,JavaScript代码是单线程的,并且只有少数操作(比如请求)可以在JavaScript代码运行的同时执行
要使处理代码在JSON请求启动后启动,只需将其推迟到JS事件循环的下一个周期。如果您确实希望在setExtent
请求和处理完成时收到通知,则可以使用延迟列表。请注意,延迟列表并没有以任何特殊方式运行延迟(延迟只是一个通知程序,而不是要运行的东西),它只是一种让您知道它们何时都已完成的方式
mainMethod(){
let bigJSONString: string = "big json string";
let xmin = 123; let ymin = 234; let xmax = 345; let ymax = 789;
let jsonExtent = new esri.geometry.Extent(...);
let setExtentDeferred = myMap.setExtent(jsonExtent);
let handleResultsDeferred = this.handleResultsDeferred(bigJSONString);
let dfd = dojo.DeferredList([setExtentDeferred, handleResultsDeferred]);
// dfd will resolve when both setExtentDeferred and handleResultsDeferred
// have resolved.
}
handleResultsDeferred(jsonString: string): dojo.Deferred {
let dfd = new dojo.Deferred();
// Run code in a setTimeout with no timeout -- the goal isn't to delay it
// for any period of time, but just to defer it until the next tick of the
// JS event loop.
setTimeout(function () {
// do work here
dfd.resolve();
});
return dfd;
}
这将为您提供如下信息:
let jsonExtent = expo.Extent
+---let setExtentDeferred = myMap.setExtent(jsonExtent);
| handleResults()
| // any other sync code runs
|
| -- current event loop cycle ends --
|
+-> // setExtent code actually runs here and starts a request
// other sync code runs
-- time passes --
// request completes
let jsonExtent = expo.Extent
+-----let setExtentDeferred = myMap.setExtent(jsonExtent);
| +---let handleResultsDeferred = handleResults()
| | // any other sync code runs
| |
| | -- current event loop cycle ends --
| |
+-|-> // setExtent code actually runs, starting a request
+-> // handle results code runs
-- time passes --
// request completes
// DeferredList resolves
您可以使用2个延迟并并行运行它们。在这两个响应返回后,您可以使用dojo/deferred/all做一些事情。如果您需要更多帮助,请发布一些代码。Philipe-谢谢您回复我。我不太确定如何执行您的建议。您需要将后端拆分为两个独立的端点。一个返回extentToZoomTo,另一个返回结果。你对后端有控制权吗?我对myMap.setExtent没有控制权。我确实可以控制HandlersSultsDerred。myMap.setExtent将导致网络调用,而HandlerResults是所有客户端JavaScript。看起来您使用的是AMD之前的dojo。检查谢谢您的回复-我尝试了您的建议,但从setExtent生成的网络调用直到处理结果后才开始。也许我做错了什么。在setExtentDeferred中,我假设还应该传回一个dojo.deferred,对吗?我应该在setExtentDeferred和HandlerResultsDeferred中调用.resolve()?由于map.setExtent本身返回一个延迟的,也许我甚至不需要setextdeferred方法。您在哪里调用
setextdeferred
方法?正如您所说,如果setExtent
返回一个deferred,则在调用它时不需要执行任何其他操作。我需要将外部上下文传递到setTimeout()中,第二个timeout参数为null。没关系,对吧?基本上就是这样:```让dfd=new dojo.DeferredList([map.setExtent(extent),this.setResultsDeferred()]),然后(()=>{//done});setResultsDeferred():dojo.Deferred{let dfd=new dojo.Deferred();let context=this;setTimeout(函数(){//一大堆工作…let x=context.getSomething();dfd.resolve();},null,collection,context);return dfd;}````是的,实际上不需要超时,所以省略它或将它设置为0是可以的。出于好奇,您将上下文参数作为参数传递给setTimeout有什么原因吗?在setTimeout中,setResults需要执行的工作需要访问以前执行的一些数据。至少在TypeScript中,我无法使用this.myData访问该数据,因为“this”指的是其他内容。因此,通过传递它,我可以绕过它。