2017-01-15 12 views
2

У меня есть приложение для веб-сопоставления, в котором есть кнопка, которая при нажатии вызывает веб-службу, которая возвращает большой JSON. Часть строки JSON представляет собой пространственную область, в которой мне нужно увеличить карту. Основные настройки JSON (раздел «Результаты» ниже) занимают несколько секунд для обработки и не связаны с разделом «extentToZoomTo». API-интерфейс отображения, который я использую, имеет метод setExtent, который возвращает объект DOJO Deferred. Метод setExtent создает изображение на сервере и может занять несколько секунд. То, что я хотел бы сделать, - вызвать метод setExtent сразу после того, как я получу JSON, и пока сервер занят работой над этим, клиентский код 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 
    } 
} 

---- EDIT --- Могу ли я запустить два Отсроченные-х параллельно каким-то образом? Код Pseudo TypeScript ниже

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

Вы можете использовать 2 Deferreds и запускать их параллельно. Вы можете использовать dojo/offfer/all, чтобы сделать что-то после возвращения 2 ответов. Поместите в некоторый код, если вам нужна дополнительная помощь. – Philippe

+0

Philipe - спасибо, что вернулись ко мне. Я не совсем уверен, как делать то, что вы предлагаете. – DevonTaig

+0

Вам нужно будет разделить бэкэнд на 2 отдельные конечные точки. Тот, который возвращает extentToZoomTo, и другой, который возвращает результаты. У вас есть контроль над бэкэнд? – Philippe

ответ

0

Отложенный объект похож на стандартное обещание JavaScript или будущее Java. Это всего лишь способ уведомления кода, когда фоновый процесс завершается. Это отличается от того, как слово «defer» используется на некоторых других языках, таких как Go, где указано, что операция откладывается до более позднего времени.

Однако метод setExtent может быть отложен до тех пор, пока не будет указан код обработки. Если код в setExtent, который делает запрос JSON, запущен в setTimeout или каким-либо другим асинхронным способом, он будет отложен до следующего тика цикла событий JavaScript, который будет после другого кода обработки результата (при условии, что он является синхронным) ,

Код setExtent фактически имеет два уровня асинхронности. Во-первых, код функции isync (поэтому вы не видите, что запрос выдается до тех пор, пока вы его не ожидаете). Второй - это сам запрос. Это будет выглядеть примерно так:

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 
+0

Благодарим за ответ. Я попробовал то, что вы предложили, но сетевой вызов, сгенерированный из setExtent, все еще не запускается до тех пор, пока результаты не будут обработаны , Может быть, я делаю что-то неправильно. В setExtentDeferred, я предполагаю, что я должен также передать обратно dojo.deferred, правильно? Должен ли я звонить .resolve() как в setExtentDeferred, так и в handleResultsDeferred? Поскольку сам map.setExtent возвращает отложенную, возможно, мне даже не нужен метод setExtentDeferred. – DevonTaig

+0

Где вы вызываете метод setExtentDeferred? Как вы говорите, если 'setExtent' возвращает отложенную, вам не нужно ничего делать при вызове. – jason0x43

+0

Я больше не звоню в setExtentDeferred, просто map.setExtent. Мне нужно было передать внешний контекст в setTimeout(), и он имеет значение null для второго аргумента таймаута. Все нормально, правда? В основном это: '' ' let dfd = new dojo.DeferredList ([map.setExtent (extent), this.setResultsDeferred()]). Then (() => { // done }); setResultsDeferred(): dojo.Deferred { let dfd = new dojo.Deferred(); let context = this; SetTimeout (функция() {// куча работы ... \t \t пусть х = context.getSomething(); \t \t dfd.resolve(); \t \t}, нулевой сбор, контекст); \t \t return dfd; } '' ' – DevonTaig