2016-01-07 4 views
2

В моем JavaScript-коде содержится немало асинхронных функций. Например, я использую D3.JS для чтения CSV-файла, и я подключаюсь к API Карт Google, чтобы найти маршруты проезда между двумя адресами.JavaScript/JQuery: лучший способ избежать вложенности асинхронных функций/анонимных функций обратного вызова

Я использую this answer в StackOverflow, чтобы дождаться завершения асинхронной функции (чтобы избежать возвратных переменных с значением undefined). Тем не менее, потому что у меня много асинхронных функций, у меня есть много вложенных анонимных функции обратного вызова:

carDirections(from, to).done(function(car) { 
     transitDirections(from, to).done(function(train) { 
     // carDirections and trainDirections have similar anonymous callback functions. 

    function carDirections(from, to) { 
     var dfd = $.Deferred(); 

     var directionsService = new google.maps.DirectionsService; 

     directionsService.route({ 
      origin: from, 
      destination: to, 
      travelMode: google.maps.TravelMode.DRIVING 
     }, function(response, status) { 
      if (status === google.maps.DirectionsStatus.OK) { 
       dfd.resolve(response.routes[0].legs[0]); 
      } 
     }); 

     return dfd.promise(); 
    } 
    // Similar code for transitDirections. 

Это приводит к спагетти из done и Deferred функций, что делает код очень трудно понять. Есть ли правильный способ решить эту проблему? Например, можно ли перепрограммировать функции Google Maps, чтобы сделать их синхронными, или есть другой (более простой) способ продолжать выполнение кода, когда предыдущая функция вернула переменную?

+0

У вас есть обещания? – blessenm

+0

@blessenm Да, это то, что я пытаюсь в сообщении выше. Это приводит к большому вложенности обещаний, и мне было интересно, можно ли этого избежать. –

ответ

0

Насколько я вижу, trainDirections не нуждается в ответе от carDirections, вам просто нужно запустить обратный вызов, когда все действия будут завершены?

Вы можете использовать отложенные объекты.

var deferredObj1 = $.Deferred(), 
    deferredObj2 = $.Deferred(); 

$.when(deferredObj1,deferredObj2).done(function(){ 
    console.log("They are both done!"); 
}); 

// inside the Event1 handler: 
deferredObj1.resolve(); 

// inside the Event2 handler: 
deferredObj2.resolve(); 

отложенное объект, введенный в JQuery 1.5, является цепной утилитой объект, созданный с помощью вызова метода jQuery.Deferred(). Он может регистрировать множественные обратные вызовы в очереди обратного вызова, вызывать обратные вызовы очереди и ретранслировать состояние успеха или отказа любой синхронной или асинхронной функции.

1

Возможно, вы захотите еще read up по теме Promise s. Вы можете цепи асинхронные операции, такие как

asyncOp1() 
    .then(result1 => asyncOp2(result1)) 
    .then(result2 => asyncOp3(result2)) 
    .then(result3 => Promise.all(asyncOp4(result3), asyncOp5(result3))) 
    .catch(error => { /* deal with an error */ }) 
    // ... 

, если все операции возвращают Promise (это поможет вам избежать вложенности). (Transpile выше псевдо-код ES5 и polyfillPromise, если вам нужно.)


Если вы ищете для различных способов решения сложного асинхронного кода ALLtogether, вы заканчивали RxJS ... не что я несу любую ответственность, если вы это сделаете :)

+0

Это похоже на приятное решение, спасибо! Однако, если у меня есть следующий код: 'carDirections (from, to) .then (car => trainDirections (from, to)), тогда (train => console.log (car));', переменная 'car' больше недоступен. Есть ли способ сохранить эту переменную, кроме отправки ее как аргумент функции? –

+0

Вы можете сохранить внешнюю переменную сферы или расширить монаду 'Promise', чтобы собрать все результаты в цепочке, но проще всего и проще всего отправить все, что вам нужно. Если вы не хотите загрязнять свою функцию поезда, вы всегда можете сделать'carOp(), затем (car => trainOp (car). Then (train => ({car, train}))), затем (({автомобиль, поезд}) => ...) '. – hon2a