2013-03-01 12 views
27

robie kilka obliczeń wewnątrz podwójnej forEach Loop coś takiego:(JavaScript) Synchronizacja forEach Loop z callbacków wewnątrz

array.forEach(function(element){ 
    Object.keys(element).forEach(function(key){ 

     /* some complex computations with asynchronous callbacks */   

    }); 
}); 

someFunctionHere(); 

czy istnieje sposób dla pętli najpierw zakończyć przed wykonaniem funkcji someFunctionHere()? lub w inny sposób, że program będzie wiedział, czy pętla zostanie zakończona, zanim przejdzie dalej someFunctionHere() ...

Może brakuje mi niektórych forów, ale te, które znalazłem, nie pomogły mi w tym, co chcę osiągnąć, a przy okazji robię to w NodeJS, także pytam, czy istnieją istniejące biblioteki, które mogą to zrobić.

Zapomniałem dodać to, czy powinno to być kolejne pytanie?

Czy istnieje sposób na synchronizację iteracji, która spowoduje przejście do następnej iteracji po zakończeniu bieżącej iteracji? (Przepraszam za to)

dzięki za pomoc ...

+1

Tak, to w zasadzie 100% gwarancją, że „someFunctionHere” zostanie wezwany przed asynchroniczne operacje są zakończone. – Pointy

+0

Odnośnie edycji: odpowiedź Golo pokazuje, w jaki sposób możesz użyć async.js do robienia tego, co chcesz. (Wierzę, że ['Clumpy.js'] (http://www.tumuski.com/code/clumpy/overview/) może być również użyty do tego.) Moja odpowiedź pokazuje, w jaki sposób można" rozwinąć własne "rozwiązanie, jeśli z jakiegoś powodu nie możesz (lub po prostu nie chcesz) korzystać z bibliotek innych firm. –

+0

Zrobiłem powyższy problem używając async.forEach(), ale zawiodło to moją bazę danych, ponieważ iteracje nie są nazywane jedną iteracją na raz (lub jeśli iteracje są wykonywane synchronicznie, może mam logiczny błąd w kodzie), więc Golo's odpowiedź może zrobić to samo, co zrobiłem w asyn.forEach(), mam na myśli od terminu "async" czy iteracje nie są wykonywane synchronicznie? – megamoth

Odpowiedz

44

przyjrzeć async.js, a zwłaszcza jego control flow oświadczenia, takie jak eachwhilst i until.

Za pomocą async.js możesz uzyskać to, co chcesz.

W aktualnej sytuacji, co chcesz, to funkcja each (który dawniej znana jako forEach), odpowiednio funkcja eachSeries który nie działa pojedynczych iteracji równolegle, ale w seryjny (patrz documentation for eachSeries szczegółów).

Aby podać przykład:

async.eachSeries([ 2, 3, 5, 7, 11 ], function (prime, callback) { 
    console.log(prime); 
    callback(); // Alternatively: callback(new Error()); 
}, function (err) { 
    if (err) { throw err; } 
    console.log('Well done :-)!'); 
}); 

To będzie iteracyjne nad tablicę liczb pierwszych i wydrukować je w odpowiedniej kolejności, jeden po drugim, i wreszcie wydrukować Well done :-)!.

+0

Dziękuję za to, ale korzystanie z biblioteki async nie jest opcją, mam na myśli, że każda iteracja w pętli powinna być naprawdę synchroniczna, ponieważ wykonuje intensywne operacje na db, że powinna to być tylko jedna iteracja na raz i zapomniałem umieścić to w pytaniu również przepraszam za to – megamoth

+0

Próbka dokładnie pokazuje to zachowanie: Każda iteracja w pętli działa synchronicznie. Jest idealnie serializowany. –

+0

Użyłem async.forEach() w moim poprzednim rozwiązaniu przed zadaniem pytania, więc iteracje w async są wykonywane po jednym na raz? ponieważ jeśli tak, mógłbym mieć pewne logiczne błędy w moim kodzie, których nie widziałem lub można to zrobić po jednym na raz, ale nie czekam na zakończenie bieżącej iteracji przed przejściem do kolejnej iteracji – megamoth

6

Można owinąć zwrotnych w zamknięciu odliczanie:

var len = array.length; 

function countdownWrapper(callback, callbackArgs) { 
    callback(callbackArgs); 
    if (--len == 0) { 
     someFunctionHere(); 
    } 
} 

array.forEach(function(element){ 
    Object.keys(element).forEach(function(key){ 

     var wrappedCallback = countdownWrapper.bind(callback); 
     /* some complex computations with asynchronous WRAPPED callbacks */ 

    }); 
}); 

Jeśli callbacks mają różną liczbę argumentów, można zrobić trochę operację arguments zamiast używania wyraźnej callbackArgs parametr.

EDIT Twoja edycja wyjaśnia, że ​​chcesz rozpocząć każde złożone obliczenie po tym, jak poprzednie obliczenia zakończą jego oddzwanianie. Może również zostać zaaranżowana przez zamknięć:

function complexOp(key, callback) { /* . . . */ } 

function originalCallback(...) { /* . . . */ } 

function doSomethingElse() { /* . . . */ } 

var iteratorCallback = (function (body, callback, completion) { 
    var i = 0, len = array.length; 
    return function iterator() { 
     callback.apply(null, arguments); 
     if (++i < len) { 
      body(array[i], iterator); 
     } else { 
      completion(); 
     } 
    }; 
}(complexOp, originalCallback, doSomethingElse)); 

// kick everything off: 
complexOp(array[0], iteratorCallback); 
+0

To jest naprawdę wielka pomoc, może rozwiązać jeden z moich problemów i nadal muszę to sprawdzić, dziękuję! – megamoth

+0

To w zasadzie nic innego, ale to, co async.js daje po wyjęciu z pudełka. Po prostu nie jest "standaryzowany", ale "zastrzeżony". –

+0

@GoloRoden - własność? To niedorzeczne. Jest mnóstwo przykładów tej sztuczki w sieci i łatwo jest wymyślić ją w locie.(Dla przypomnienia, nie skopiowałem kodu z mojego postu z dowolnego miejsca.) Punktem postu jest to, że nie potrzebujesz całego pakietu, jak [async.js] (https://github.com/caolan/ async) lub [Clumpy.js] (http://www.tumuski.com/code/clumpy/overview/); to tylko kilka linii kodu. –

1

HIGH PERFORMANCE ROZWIĄZANIE:

Nie może być przypadek, kiedy może chcesz/pozwalają przetworzyć tablicę asynchronicznie/równolegle, ale chcą funkcją nazywać po wszyscy członkowie forEach zostali przetworzeni. Przykład:

var async = require('async'); 

async.forEach(array,function(elementOfArray, callback){ 
    //process each element of array in parallel 
    // .. 
    // .. 
    // .. 
    callback(); //notify that this iteration is complete 
}, function(err){ 
if(err){throw err;} 
console.log("processing all elements completed"); 
}); 

Stąd ten sposób można wykonać bez blokowania procesora intensywne operacje.

UWAGA: Gdy używasz eachSeries dla ogromnych tablic, wiele iteracyjnych wywołań zwrotnych może przepełnić stos.

przeczytać tutaj: https://github.com/caolan/async#control-flow

1

Dla przeglądarek, które wspierają obietnicy (lub za pomocą PolyFill)/nodejs I zostały wdrożone moje ja synchronizacji wersję forEach z wywołania zwrotne, więc będę po prostu dzielić się tutaj ..

Wywołanie zwrotne musi powrócić obietnicę ..

function forEachSync(array, callback) { 
    let lastIndex = array.length - 1; 
    let startIndex = 0; 

    return new Promise((resolve, reject) => { // Finish all 
     let functionToIterateWith = (currIndex) => { 
      if (currIndex > lastIndex) { 
       return resolve(); 
      } else { 
       callback(array[currIndex]).then(() => { 
        functionToIterateWith(currIndex + 1); 
       }).catch(err => reject(err)); 
      } 
     } 

     functionToIterateWith(startIndex); 
    }); 
} 
Powiązane problemy