2014-04-17 3 views
0

МОЙ ОТВЕТ НИЖЕ ИМЕЕТ РАБОЧАЯ КОДА И объяснениеосмыслении Asynchronous JavaScript (Google Maps)


Я пытаюсь получить мою голову вокруг асинхронного программирования путем создания Путешествуя Salesperson алгоритм с с помощью API карт Google.

Вот как это должно работать:

  1. У нас есть список адресов строк addresses
  2. Для каждой комбинации адреса [я] и [J] вычислим расстояние, используя Google DirectionsService и мы сохраняем это в объекте Javascript (distanceTable).
  3. tspSolver должен работать после того, как у нас есть все расстояния (еще не реализованы).

Мой наивный код ниже. Как вы можете видеть, есть несколько вопросов, связанных с обратными вызовами:

Проходит directionResults в качестве обратного вызова направления. Сервис. Он правильно вычисляет расстояние, но поскольку я больше не внутри цикла, чтобы построить distanceTable, я не могу сохранить результат правильно. Я прокомментировал раздел, который должен его хранить, но это, очевидно, работает только внутри цикла.

Я передаю свой tspSolver в качестве обратного вызова для allDistances. Однако я замечаю, что он выполняется, пока расстояния не рассчитаны на directionResults. Я предполагаю, что мне нужно сделать какую-то форму вложенности обратных вызовов?

Кто может помочь мне разобраться в этом.

gMap.directionsService = new google.maps.DirectionsService(); 

var addresses = ['Dam, Amsterdam','Spui, Amsterdam','Middenweg, Amsterdam']; 
var distanceTable = {}; 
//all combinations of addresses with distances, for use in TSP algorithm 
//{ 
// addressA { 
// addressB: 2000 
// addressC: 2500 
// } 
// addressB { 
// addressC: 1800 
// } 


function tspSolver(distanceTable) { 
    console.log('Distances are there, now for some clever TSP algorithm') 
    //this should only be executed after the distances are returned. 
} 

function allDistances(addresses, callback) { 
    for(var i=0; i<addresses.length; ++i) { 
    distanceTable[addresses[i]] = {}; 
    for(var j=i+1; j<addresses.length; ++j) { 
     // Compose request for every pair of addresses (one way) 
     var request = { 
     origin: addresses[i], 
     destination: addresses[j], 
     travelMode: 'DRIVING' 
     }; 
     console.log(request); 
     gMap.directionsService.route(request, directionResults); 
    } 
    } 
    callback(distanceTable) 
} 


function directionResults(result, status) { 
    console.log("Receiving request for route"); 
    console.log(result); 
    if (status == google.maps.DirectionsStatus.OK) { 
    var totalDistance = 0; 
    var legs = result.routes[0].legs; 
    for(var i=0; i<legs.length; ++i) { 
     totalDistance += legs[i].distance.value; 
    } 
    console.log(totalDistance); 
    // I really want to add it to the distanceTable... 
    //distanceTable[addresses[i]][addresses[j]] = totalDistance; 
    } 
} 


//call function to start solving 
function executeTSP() { 
    allDistances(addresses, tspSolver); 
} 

ответ

1

Я предполагаю, что вы должны передать функцию обратного вызова с запросом

Обратный путь, чтобы продолжить выполнение, когда вы программируете в асинхронном.

Псевдо пример:

function doAcync(callback) 
{ 
//gets result 
callback(result); 
} 

function one() 
{ 
    doAcync(two); 
} 

function two(result) 
{ 
    doAcync(three); 
} 

function three(result) 
{ 
    // continue with your live 
} 
+0

Так что же такое «результат», который передается функциям два и три? Извините, но у меня возникают проблемы с этим. –

+1

Не стоит беспокоиться о большинстве ppl. результатом является то, что было получено из acyncronious вызова, это своего рода соглашение о том, что, если вы передаете обратный вызов и функцию, с которой вы передаете обратный вызов, имеет возвращаемое значение, тогда функция обратного вызова будет вызываться с этим возвращаемым значением. –

+0

Я думаю, что ваш шаблон поможет мне решить мою вторую проблему (порядок исполнения). Я не уверен, что это поможет мне написать результаты в distanceTable ... –

1

Решение моей первой проблемы заключается в «закрытия внутри петли». Я отслеживаю свою петлю с помощью счетчика (скорее всего, 2 счетчика в моем случае). К тому моменту, когда выполняется асинхронная функция/замыкание, цикл завершится.

Это приведет к поражению всех попыток доступа к счетчикам циклов из-за закрытия: вы всегда получите максимальное число.

Решение, которое я выбрал, состояло в том, чтобы завернуть замыкание в другую функцию: так называемую «анонимную упаковку». Эта оболочка содержит ссылку, на которую не влияет цикл.

некоторые лучше объяснения можно найти здесь: Mutable variable is accessible from closure. How can I fix this? И здесь: http://bonsaiden.github.io/JavaScript-Garden/#function.closures

Чтобы получить вторую задачу (порядка исполнения) фиксированный, я прибегал к выполнению обратного вызова в функции обратного вызова.Некоторый, по общему признанию, уродливый код отслеживает, что обратный вызов вызывается только при последней итерации. Я уверен, что есть более элегантные способы справиться с этим, но на данный момент это работает:

var directionsService; 

function initialize() { 
    directionsService = new google.maps.DirectionsService(); 
} 

var addresses = ['Dam, Amsterdam','Spui, Amsterdam','Middenweg, Amsterdam']; 
var distanceTable = {}; 
//all combinations of addresses with distances, for use in TSP algorithm 
//{ 
// addressA { 
// addressB: 2000 
// addressC: 2500 
// } 
// addressB { 
// addressC: 1800 
// } 


function tspSolver(distances) { 
    //this function is called as a callback from allDistances 
    for(var i=0; i<addresses.length; ++i) { 
    for(var j=i+1; j<addresses.length; ++j) { 
     console.log(addresses[i]+' to '+addresses[j]+': '+distances[addresses[i]][addresses[j]]) 
    } 
    } 
} 

function allDistances(addresses, callback) { 
    for(var i=0; i<addresses.length; ++i) { 
    distanceTable[addresses[i]] = {}; 
    for(var j=i+1; j<addresses.length; ++j) { 
     var request = { 
     origin: addresses[i], 
     destination: addresses[j], 
     travelMode: 'DRIVING' 
     }; 
     console.log(request); 
     //anonymous wrapper around closure to preserve the loop counters: i,j -> e,f 
     (function(e,f) {directionsService.route(request, function(result,status) { 
      if (status == google.maps.DirectionsStatus.OK) { 
      //calculate totalDistance as sum of the legs and store the result 
      var totalDistance = 0; 
      var legs = result.routes[0].legs; 
      for(var x=0; x<legs.length; ++x) { 
       totalDistance += legs[x].distance.value; 
      } 
      distanceTable[addresses[e]][addresses[f]] = totalDistance; 
      } 
     //trigger the callback on the last iteration only 
     if (e+2 == addresses.length) 
      { 
      callback(distanceTable) 
      } 
     }); 
     })(i,j); 
     //end of wrapper 
    } 
    } 
} 

function executeTSP(){ 
    allDistances(addresses, tspSolver) 
}