Javascript'te bazı eşzamansız görevlerin tamamlanmasını beklemenin en basit yolu?


112

Bazı mongodb koleksiyonlarını bırakmak istiyorum, ama bu eşzamansız bir görev. Kod şu şekilde olacaktır:

var mongoose = require('mongoose');

mongoose.connect('mongo://localhost/xxx');

var conn = mongoose.connection;

['aaa','bbb','ccc'].forEach(function(name){
    conn.collection(name).drop(function(err) {
        console.log('dropped');
    });
});
console.log('all dropped');

Konsol şunları görüntüler:

all dropped
dropped
dropped
dropped

all droppedTüm koleksiyonlar bırakıldıktan sonra basılacağından emin olmanın en basit yolu nedir ? Kodu basitleştirmek için herhangi bir üçüncü taraf kullanılabilir.

Yanıtlar:


92

Kullandığınızı görüyorum, mongoosebu nedenle sunucu tarafı JavaScript'ten bahsediyorsunuz. Bu durumda asenkron modüle bakmanızı ve kullanmanızı tavsiye ederim async.parallel(...). Bu modülü gerçekten yararlı bulacaksınız - mücadele ettiğiniz sorunu çözmek için geliştirildi. Kodunuz şöyle görünebilir

var async = require('async');

var calls = [];

['aaa','bbb','ccc'].forEach(function(name){
    calls.push(function(callback) {
        conn.collection(name).drop(function(err) {
            if (err)
                return callback(err);
            console.log('dropped');
            callback(null, name);
        });
    }
)});

async.parallel(calls, function(err, result) {
    /* this code will run after all calls finished the job or
       when any of the calls passes an error */
    if (err)
        return console.log(err);
    console.log(result);
});

Bununla ... forEach yöntemi eşzamansız gerçekleşir. Öyleyse, nesne listesi burada detaylandırılan 3'ten daha uzun olsaydı, async.parallel (çağrılar, işlev (err, sonuç)) değerlendirildiğinde çağrıların henüz orijinal listedeki tüm işlevleri içermemesi söz konusu olamaz mı?
Martin Beeby

5
@MartinBeeby forEacheşzamanlıdır. Şuraya bir göz atın: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…forEach Altta uygulaması var . Geri aramanın olduğu her şey eşzamansız değildir.
acayip

2
Kayıt için asenkron bir tarayıcıda da kullanılabilir.
Erwin Wessels

@MartinBeeby Geri arama ile her şey eşzamansızdır, sorun şu ki forEach bir "geri arama" değil, sadece normal bir işlev (Mozilla tarafından terminolojinin yanlış kullanımı) geçiriliyor. İşlevsel bir programlama dilinde, geçirilen bir işlevi asla "geri arama" olarak adlandırmazsınız

3
@ ghert85 Hayır, terminolojide yanlış bir şey yok. Geri arama, başka bir koda bağımsız değişken olarak iletilen ve bir noktada çalıştırılması beklenen herhangi bir çalıştırılabilir koddur. Standart tanım budur. Ve eşzamanlı veya eşzamansız olarak çağrılabilir. Şuna
acayip

128

Sözleri kullanın .

var mongoose = require('mongoose');

mongoose.connect('your MongoDB connection string');
var conn = mongoose.connection;

var promises = ['aaa', 'bbb', 'ccc'].map(function(name) {
  return new Promise(function(resolve, reject) {
    var collection = conn.collection(name);
    collection.drop(function(err) {
      if (err) { return reject(err); }
      console.log('dropped ' + name);
      resolve();
    });
  });
});

Promise.all(promises)
.then(function() { console.log('all dropped)'); })
.catch(console.error);

Bu, her bir koleksiyonu düşürür, her birinin ardından "bırakılan" yazdırır ve tamamlandığında "tümü bırakılan" yazdırır. Bir hata meydana gelirse, görüntülenir stderr.


Önceki cevap (bu, Node'un Promises için yerel desteğinden önce tarihlenmektedir):

Q vaatlerini veya Bluebird vaatlerini kullanın .

İle Q :

var Q = require('q');
var mongoose = require('mongoose');

mongoose.connect('your MongoDB connection string');
var conn = mongoose.connection;

var promises = ['aaa','bbb','ccc'].map(function(name){
    var collection = conn.collection(name);
    return Q.ninvoke(collection, 'drop')
      .then(function() { console.log('dropped ' + name); });
});

Q.all(promises)
.then(function() { console.log('all dropped'); })
.fail(console.error);

İle Bluebird :

var Promise = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));

mongoose.connect('your MongoDB connection string');
var conn = mongoose.connection;

var promises = ['aaa', 'bbb', 'ccc'].map(function(name) {
  return conn.collection(name).dropAsync().then(function() {
    console.log('dropped ' + name);
  });
});

Promise.all(promises)
.then(function() { console.log('all dropped'); })
.error(console.error);

1
Sözler, gitmenin yoludur. Bluebird , performans açısından kritik koddaysa iyi çalışacak başka bir vaat kitaplığıdır. Drop-in yerine geçmelidir. Sadece kullan require('bluebird').
weiyin

Bir Bluebird örneği ekledim. Bluebird'ü kullanmanın en iyi yolu özelliği kullanmak olduğu için biraz farklı promisifyAll.
Nate

PromisifyAll'ın nasıl çalıştığı hakkında herhangi bir fikriniz ... Dokümanları okudum ama anlamadım, bunun gibi parametrelere sahip olmayan fonksiyonları nasıl işlediği function abc(data){, çünkü bu gibi değil function abc(err, callback){...Temelde tüm fonksiyonların ilk param olarak hata ve 2. param olarak geri çağırma alacağını düşünmüyorum
Muhammed Umer


MongoDB sürücüsünün de vaatleri desteklemesinin üzerinden bir süre geçti. Bundan yararlanmak için örneğinizi güncelleyebilir misiniz? .map(function(name) { return conn.collection(name).drop() })
djanowski

21

Bunu yapmanın yolu, görevlere paylaşılan bir sayacı güncelleyen bir geri arama iletmektir. Paylaşılan sayaç sıfıra ulaştığında, normal akışınıza devam edebilmeniz için tüm görevlerin bittiğini bilirsiniz.

var ntasks_left_to_go = 4;

var callback = function(){
    ntasks_left_to_go -= 1;
    if(ntasks_left_to_go <= 0){
         console.log('All tasks have completed. Do your stuff');
    }
}

task1(callback);
task2(callback);
task3(callback);
task4(callback);

Elbette, bu tür bir kodu daha genel veya yeniden kullanılabilir hale getirmenin birçok yolu vardır ve dışarıdaki pek çok eşzamansız programlama kitaplığından herhangi birinin bu tür şeyleri yapmak için en az bir işlevi olmalıdır.


Bu, uygulaması en kolay olmayabilir, ancak harici modüller gerektirmeyen bir yanıt görmeyi gerçekten seviyorum. Teşekkür ederim!
counterbeing

8

@ Freakish cevabını genişleterek, async ayrıca sizin durumunuz için özellikle uygun görünen her bir yöntemi sunar:

var async = require('async');

async.each(['aaa','bbb','ccc'], function(name, callback) {
    conn.collection(name).drop( callback );
}, function(err) {
    if( err ) { return console.log(err); }
    console.log('all dropped');
});

IMHO, bu, kodu hem daha verimli hem de daha okunaklı hale getirir. Kaldırmak için özgürdüm console.log('dropped')- eğer isterseniz, bunun yerine şunu kullanın:

var async = require('async');

async.each(['aaa','bbb','ccc'], function(name, callback) {
    // if you really want the console.log( 'dropped' ),
    // replace the 'callback' here with an anonymous function
    conn.collection(name).drop( function(err) {
        if( err ) { return callback(err); }
        console.log('dropped');
        callback()
    });
}, function(err) {
    if( err ) { return console.log(err); }
    console.log('all dropped');
});

5

Bunu dışsal özgürlükler olmadan yapıyorum:

var yourArray = ['aaa','bbb','ccc'];
var counter = [];

yourArray.forEach(function(name){
    conn.collection(name).drop(function(err) {
        counter.push(true);
        console.log('dropped');
        if(counter.length === yourArray.length){
            console.log('all dropped');
        }
    });                
});

4

Tüm cevaplar oldukça eskidir. 2013'ün başından bu yana Mongoose , tüm sorgular için vaatleri kademeli olarak desteklemeye başladı , bu yüzden ileriye dönük olarak birkaç asenkron çağrıyı gereken sırada yapılandırmanın önerilen yolu bu olacaktı.


0

İle deferred(başka söz / ertelenmiş uygulama) Yapabileceğiniz:

// Setup 'pdrop', promise version of 'drop' method
var deferred = require('deferred');
mongoose.Collection.prototype.pdrop =
    deferred.promisify(mongoose.Collection.prototype.drop);

// Drop collections:
deferred.map(['aaa','bbb','ccc'], function(name){
    return conn.collection(name).pdrop()(function () {
      console.log("dropped");
    });
}).end(function () {
    console.log("all dropped");
}, null);

0

Babel veya bu tür aktarıcılar kullanıyorsanız ve async / await kullanıyorsanız şunları yapabilirsiniz:

function onDrop() {
   console.log("dropped");
}

async function dropAll( collections ) {
   const drops = collections.map(col => conn.collection(col).drop(onDrop) );
   await drops;
   console.log("all dropped");
}

Bir Söz'e geri aranamaz drop()ve geri dönmeyi bekleyemezsiniz. Lütfen bu örneği düzeltip kaldırır onDropmısınız?
djanowski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.