2015-07-10 11 views
5

Chcę zapisać 8 obiektów do bazy danych MongoDB za pomocą mangusty. Po zapisaniu ostatniego dokumentu chcę zgłosić (tzn. Wysłać wydarzenie), że wszystkie dokumenty zostały zapisane.Zapisywanie wielu dokumentów za pomocą mangusty i robienie czegoś, gdy ostatni raz jest zapisany.

Sposób, w jaki teraz to robię jest dość brudny (szczególnie w przypadku coraz większej ilości dokumentów, które chcę zapisać).

Oto, jak to mam teraz (tylko dla 4 osób w tym przykładzie). Czy istnieje jakiś czystszy sposób, który możesz polecić?

person1.save(function(err, result){ 
    if (err) console.log(err); 
    else{ 
     person2.save(function(err, result){ 
      if (err) console.log(err); 
      else{ 
       person3.save(function(err, result){ 
        if (err) console.log(err); 
        else{ 
         person4.save(function(err, result){ 
          if (err) console.log(err); 
          else{ 
           done(); 
          } 
         }); 
        } 
       }); 
      } 
     }); 
    } 
}); 

Odpowiedz

12

Przydatny biblioteki do koordynowania operacji asynchronicznych jest async. W twoim przypadku, kod będzie wyglądał tak:

var people = [ person1, person2, person3, person4, ... ]; 

async.eachSeries(people, function(person, asyncdone) { 
    person.save(asyncdone); 
}, function(err) { 
    if (err) return console.log(err); 
    done(); // or `done(err)` if you want the pass the error up 
}); 
+1

To rozwiązanie wymaga dodatkowej biblioteki, dzięki czemu aplikacja jest bardziej ciężka. Nadal jest to dobre rozwiązanie. Daję + –

+0

Biblioteka asynchroniczna nie jest ważnym dodatkiem. Ponieważ jest to po stronie serwera, wartość asynchronizmu w porównaniu z niewielkim zasięgiem nie ma negatywnego wpływu na kod. –

+0

Dla czegoś takiego, tak naprawdę chcesz używać równolegle. –

0

Chciałbym użyć Underscore Each do iteracji na tablicy modeli People. Doprowadziłoby to do tego, że Twój kod byłby czystszy i pomógłbyś uniknąć kodu "boomerang effect".

Z documentation:

.each (lista, iteratee [okolicznościach]) alias: forEach

iteracyjnie listę elementów, dostarczające każdy z kolei funkcją iteratee. Iteratee jest związany z obiektem kontekstu, jeśli został przekazany. Każde wywołanie iteratee wywoływane jest trzema argumentami: (element, indeks, lista). Jeśli list jest obiektem JavaScript, argumentami iteratee będą (wartość, klucz, lista). Zwraca listę do łączenia.

Na przykład:

var people = [person1, person2]; 
var length = people.length; 
var hasError = false; 

var doSomething = function() { 
    console.log('I'm done'); 
} 

var doSomethingElse = function() { 
    console.log('There was an error'); 
} 

_.each(people, function(person, i) { 

    if (!hasError) { 

     person.save(function(err, result) { 
      if (err) { 
       console.log(err); 
       hasError = true; 
      } 
     ); 

     if (i === length) { 
      doSomething(); 
     } 

    } else { 

     // There was an error 
     doSomethingElse(); 
    } 

}); 
+0

To wywoła oddzwanianie, gdy kwota X Zapisz operacje są _started_, ale to nie musi oznaczać, że wszystkie zakończone. Ponadto, dlaczego nie po prostu użyć 'Array # forEach'? – robertklep

+0

Proszę zobaczyć moją zaktualizowaną odpowiedź dotyczącą wykończenia operacji. – aaronmarruk

+0

Aby odpowiedzieć na twoje pytanie dotyczące Podkreślenia, wolę wygodę i prostotę/czytelność metody podkreślenia.aach, w porównaniu do zwykłych pętli javascript. – aaronmarruk

2

Polecam mieć tablicę i zapisać z iteracji. Będzie miał taką samą wydajność, ale kod będzie czystszy.

Można mieć

var Person = mongoose.model('Person'); 
var people = []; 

people[0] = new Person({id: 'someid'}); 
people[0].set('name', 'Mario'); 
people[1] = new Person({id: 'someid'}); 
people[1].set('name', 'Mario'); 
people[2] = new Person({id: 'someid'}); 
people[2].set('name', 'Mario'); 

var errors = []; 
for(person in people){ 
people[person].save(function(err, done){ 
    if(err) errors.push(err); 
    if (person === people.length){ yourCallbackFunction(errors){ 
    if (errors.length!=0) console.log(errors); 
    //yourcode here 
    }; 
    } 
}); 
} 
+1

Istnieje jednak niewielka różnica: twój kod zapisuje dokumenty równolegle, podczas gdy oryginalny przykład zapisał dokumenty sekwencyjnie. – robertklep

+0

Autor opisu, który nie wspomniał o tej sekwencji, jest ważny, ale aktualizuję moje ans, aby sprawdzić, czy wszystkie są dobrze wykonane. –

+0

@ PawełSmołka Przypuszczam, że nie ma znaczenia, czy jest on zapisywany jednocześnie, po prostu chcę wiedzieć, kiedy ostatni zostanie zapisany. – CodyBugstein

0

Najlepszym sposobem jest użycie asynchronicznych waterfall. Część fragmentu kodu może wyglądać jak poniżej. Proszę zapoznać się z powyższym linkiem. Przełamuje on asynchroniczną naturę i przekształca się w jeden po drugim procesie (jeśli się nie mylę).

waterfall([ 
    function(callback){ 
callback(null, 'one', 'two'); 
}, 
    function(arg1, arg2, callback){ 
    callback(null, 'three'); 
    }, 
    function(arg1, callback){ 
// arg1 now equals 'three' 
    callback(null, 'done'); 
    } 
], function (err, result) { 
    // result now equals 'done' 
}); 
+0

Kaczor Donalda, świetny, właśnie edytowałeś moje literówki. –

3

Korzystanie obietnice i Array.map()

const userPromises = persons.map(user => { 
    return new Promise((resolve, reject) => { 
    person.save((error, result) => { 
     if (error) { 
     reject(error) 
     } 
     resolve(result); 
    }) 
    }) 
}); 

Promise.all(userPromises).then((results) => { 
    //yay! 
    //results = [first, second, etc...] 
}, (error) => { 
    //nay! 
})