2015-10-09 4 views
0

Я разрабатываю расширение chrome, которое использует две вложенные функции async. Однако я хотел, чтобы функции были синхронизированы, но не существует параметра, который делает его синхронизацией, API имеет только опцию async. Мне нужно, чтобы две функции имели поведение вложенных функций синхронизации, если это возможно. Скажи мне, если это имеет для тебя какой-то смысл. Вот основные части кода:Сделать вложенные функции Async Вложенные функции синхронизации

// Track the number of callbacks from chrome.history.getVisits() 
// that we expect to get. When it reaches zero, we have all results. 
chrome.history.search({ 
     'text': '',    // Return every history item.... 
     'startTime': oneWeekAgo, // that was accessed less than one week ago. 
     'maxResults': 999999999 
    }, 
    function (historyItems) { 
     // For each history item, get details on all visits.; 

     for (var i = 0; i < historyItems.length; ++i) { 
      Edited - 0 - I need some code here too. 
      chrome.history.getVisits({url: historyItems[i].url}, function   (visitItems) { 
       //1 - I want this to happen first, but it happens after 2 

       for (var j = 0; j < visitItems.length; ++j) { 
        //Do something that depends on the first function 
       } 
      }) 
      //2 -i want this to happen second and after 1, but it happens first, since chrome.history.getvisits is async. 
     } 
     //3- I want this to happen third, when the two loops are complete. 
    }) 
+0

Хотя невозможно сделать магическую синхронизацию, вы можете сделать это с помощью [Обещания] (http://www.html5rocks.com/en/tutorials/es6/promises/). – Xan

ответ

2

JavaScript неотъемлемо однопоточный, и «асинхронный» означает «где-то в конце очереди событий», на самом деле.

Из-за этого нет возможности подождать, пока задача async закончится: вы должны завершить работу для следующей задачи async для запуска. Единственный способ - добавить код, который будет вызываться в конце задачи async, называемой цепочкой async (или callback).

Однако, к счастью, существуют рамки, которые помогут вам построить этот материал более организованным образом. Одной из таких рамок является Promises. Я оставлю вас посетить эту ссылку для базового введения.

Во-первых, давайте «обещаем» требуемые вызовы API. Первый из них:

function historyLastWeek() { 
    return new Promise(function(resolve, reject) { 
    chrome.history.search({ 
     'text': '', 
     'startTime': oneWeekAgo, // don't forget that part 
     'maxResults': 999999999 
    }, function(historyItems) { 
     if(chrome.runtime.lastError) { 
     reject(chrome.runtime.lastError.message); 
     } else { 
     resolve(historyItems); 
     } 
    }); 
    }); 
} 

Приведенный выше код возвращает Promise, который будет выполняться вызов chrome.history.search API и либо решимость с деталями истории, или отклонять с сообщением об ошибке.

Пункт обещания состоит в том, что вы можете использовать .then() на нем, цепочки звонков.

Давайте также promisify chrome.history.getVisits (примечание, это занимает пункт истории, так как мы хотим):

function getVisits(historyItem) { 
    return new Promise(function(resolve, reject) { 
    chrome.history.getVisits({url: historyItem.url}, function(visitItems) { 
     if(chrome.runtime.lastError) { 
     reject(chrome.runtime.lastError.message); 
     } else { 
     resolve({ 
      historyItem: historyItem, // let's keep track of it 
      visitItems: visitItems 
     }); 
     } 
    }); 
    }); 
} 

Итак, у нас есть 2 обещания, которые возвращают массив результатов. Как до tie them together?

Во-первых, я предполагаю, что вы не нарушаете внутренний цикл (поэтому я запускаю «getVisits» в «параллельном»). Для этого у нас есть Promise.all. Давайте посмотрим ..

historyLastWeek().then(function(historyItems) { 
    return Promise.all(
    // map() returns the array of results of applying a function 
    // to all members of the array 
    historyItems.map(getVisits) 
); 
}).then(function(results) { 
    // results is an array of objects 
    results.each(function(result) { 
    // here, result.historyItem is the history item, 
    // and result.visitItems is an array of visit items 
    /* here goes your code #1 */ 
    result.visitItems.each(function(visitItem) { 
     /* here goes your "Do something that depends on the first function" code */ 
    }); 
    /* here goes your code #2 */ 
    }); 
    /* here goes your code #3 */ 
}).catch(function(errorMsg) { 
    // oh noes 
}); 

Если вам нужно сделать что-то после кода # 3, то вам необходимо promisify той последней функции, а также, и добавить еще один .then().


Этот код имеет неудачное свойство: поскольку JavaScript не лень, all() будет собирать все результаты в одной монолитной 2-мерного массива перед любой из кода выполняется, и вы не можете прервать внутренний цикл рано.

Вы можете изменить это на execute sequentially, вместо того чтобы собирать массив, а затем обрабатывать его.

historyLastWeek().then(function(historyItems) { 
    return historyItems.reduce(function(sequence, historyItem) { 
    return sequence.then(function() { 
     return getVisits(historyItem); 
    ).then(function(result) { 
     // here, result.historyItem is the history item, 
     // and result.visitItems is an array of visit items 
     /* here goes your code #1 */ 
     result.visitItems.each(function(visitItem) { 
     /* here goes your "Do something that depends on the first function" code */ 
     }); 
     /* here goes your code #2 */ 
     // Gotta return some promise 
     return Promise.resolve(); 
    }); 
    }, Promise.resolve()); 
}).then(function() { 
    /* here goes your code #3 */ 
}).catch(function(errorMsg) { 
    // oh noes 
}); 

См. Ссылку выше для объяснения того, как все это работает.

Это спорно, если это проще и чище, чем не-Promise кода, но, по крайней мере, это альтернатива ручного построения цепочки обратных вызовов.

+0

Спасибо за отзыв. Однако мой мозг просто взорвался. Думаю, для меня все это займет несколько дней. Пока я не все понял, я не смогу отметить как принятый ответ, но как только смогу, я скажу, работает ли он для меня. – giancarloap

1

Если вы хотите поддерживать версии Chrome, прежде чем 32, где были введены обещания, единственное, что вы можете сделать, это переместить код 2 и 3 во внутренний обратный вызов :

function (historyItems) { 
    var lastItemIndex = historyItems.length - 1; 
    for (var i = 0; i <= lastItemIndex; ++i) { 
     chrome.history.getVisits({url: historyItems[i].url}, function(visitItems) { 
      // 1 
      for (var j = 0; j < visitItems.length; ++j) { 
       //Do something that depends on the first function 
      } 
      // 2 
      ...................... 
      if (i == lastItemIndex) { 
       // 3 
       ...................... 
      } 
     }) 
    } 
}) 
+0

Это .. Не вещь. Обещания кажутся идеальным инструментом в этой ситуации. – Xan

+0

Кроме того, [править] для даже «многие люди используют Chrome 2 основных версии позади» – Xan

+0

Хорошо. Я думаю, что сегодня я добавлю решение, основанное на обещании, это хорошо, чтобы немного поработать. – Xan

Смежные вопросы