如何强制Deferred方法立即执行?

时间:2017-01-15 15:46:08

标签: javascript performance asynchronous dojo deferred

我有一个Web地图应用程序,它有一个按钮,当点击它时会调用一个返回大型JSON的Web服务。 JSON字符串的一部分表示我需要将地图缩放到的空间范围。 JSON的主要内容("结果"以下部分)需要几秒钟的时间来处理,并与" extentToZoomTo"无关。部分。我使用的映射API有一个setExtent方法,它返回一个 DOJO Deferred 对象。 setExtent方法在服务器上生成图像 ,可能需要几秒钟才能完成。我想要做的是在获得JSON后立即调用setExtent方法,并且当服务器忙于处理时,浏览器上的客户端javaScript代码可以处理"结果" JSON的一部分同时出现。我怎么能做到这一点?我发现如果我在开始处理"结果之前简单地调用setExtent"部分,它实际上并没有将该请求发送到服务器,直到结束部分已经完全处理完之后。我可以通过检查开发人员工具中的“网络”选项卡来验证该行为(当我获得JSON和调用setExtent方法之间存在几秒钟的差距)。我猜这与setExtent延期这一事实有关。我怎么才能马上开火呢?

{
  "extentToZoomTo": {
    "xmin": 1234,
    "ymin": 4567,
    "xmax": 2345,
    "ymax": 5678
  },
  "results": {
          //A very large amount of data here that takes several seconds to process
  }
}

----编辑--- 我可以以某种方式同时运行两个Deferred&?s?下面的伪类型代码

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

1 个答案:

答案 0 :(得分:0)

Deferred对象类似于标准的JavaScript Promise或Java Future。它只是在后台进程完成时通知代码的一种方式。这与单词" 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

同步操作按顺序运行,任何异步操作都会延迟一段时间。

要使某些内容并行运行,您需要在发出请求后,但在它完成之前启动处理代码。请注意,请求是其中唯一可以与其他任何内容并行运行的部分。除了像服务工作者这样的新功能之外,JavaScript代码是单线程的,只有少数操作(如请求)可以在JavaScript代码运行的同时实际执行操作。

要在启动JSON请求后启动处理代码,您只需将其推迟到JS事件循环的下一个滴答。如果您确实希望在setExtent请求处理完成时收到通知,则可以使用DeferredList。请注意,DeferredList不以任何特殊方式运行延迟(Deferred只是一个通知程序,而不是要运行的东西),它只是一种让你知道它们何时完成的方式。

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