Javascript에서 비동기 작업이 완료되기를 기다리는 가장 간단한 방법은 무엇입니까?


112

mongodb 컬렉션을 삭제하고 싶지만 비동기 작업입니다. 코드는 다음과 같습니다.

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');

콘솔에 다음이 표시됩니다.

all dropped
dropped
dropped
dropped

all dropped모든 컬렉션이 삭제 된 후 인쇄 되도록하는 가장 간단한 방법은 무엇입니까 ? 타사를 사용하여 코드를 단순화 할 수 있습니다.

답변:


92

나는 당신이 사용하고 mongoose있으므로 서버 측 JavaScript에 대해 이야기하고 있습니다. 이 경우의 I 조언에보고 비동기 모듈 및 사용 async.parallel(...). 이 모듈은 정말 도움이 될 것입니다. 문제를 해결하기 위해 개발되었습니다. 코드는 다음과 같습니다.

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);
});

이것으로 ... forEach 메서드는 비동기 적으로 발생합니다. 따라서 객체 목록이 여기에 설명 된 3 개보다 길다면 async.parallel (calls, function (err, result)가 평가 될 때 호출이 아직 원래 목록의 모든 함수를 포함하지 않는 경우가 아닐까요?
Martin Beeby 2013 년

5
@MartinBeeby forEach는 동기식입니다. 여기에서보세요 : developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…forEach 하단에 의 구현이 있습니다 . 콜백이있는 모든 것이 비동기적인 것은 아닙니다.
freakish

2
기록을 위해 브라우저에서도 비동기를 사용할 수 있습니다.
Erwin Wessels 2014 년

@MartinBeeby 콜백이있는 모든 것은 비동기식입니다. 문제는 forEach가 "콜백"이 아니라 일반 함수 (Mozilla의 용어를 잘못 사용함)로 전달된다는 것입니다. 기능적인 프로그래밍 언어에서는 전달 된 함수는 "콜백"호출하지 않을 것

3
@ ghert85 아니요, 용어에는 문제가 없습니다. 콜백은 단순히 다른 코드에 대한 인수로 전달되고 어느 시점에서 실행될 것으로 예상되는 실행 가능한 코드입니다. 그것이 표준 정의입니다. 그리고 동기식 또는 비동기식으로 호출 할 수 있습니다. 이것을보십시오 : en.wikipedia.org/wiki/Callback_(computer_programming)
freakish

128

약속을 사용하십시오 .

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);

이렇게하면 각 컬렉션을 삭제하고 각 컬렉션 후에 "dropped"를 인쇄 한 다음 완료되면 "all drop"을 인쇄합니다. 오류가 발생하면에 표시됩니다 stderr.


이전 답변 (이는 Promises에 대한 Node의 기본 지원 이전 날짜) :

Q promise 또는 Bluebird promise를 사용하세요 .

함께 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);

블루 버드 :

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
약속은 갈 길이다. Bluebird 는 이것이 성능이 중요한 코드 인 경우 잘 작동하는 또 다른 약속 라이브러리입니다. 드롭 인 교체 여야합니다. 사용하십시오 require('bluebird').
weiyin

Bluebird 예제를 추가했습니다. Bluebird를 사용하는 가장 좋은 방법은 promisifyAll기능 을 사용하는 것이기 때문에 약간 다릅니다 .
Nate

promisifyAll이 어떻게 작동하는지 .. 나는 문서를 읽었지만 나는 그것이 매개 변수가 아닌 함수를 처리하는 방법을 이해하지 못한다. 기본적으로 모든 함수가 첫 번째 매개 변수로 오류가 발생하고 두 번째 매개 변수로 콜백이라고 생각 function abc(data){하지 않는다.function abc(err, callback){...
Muhammad Umer 2015

에서 세부 @MuhammadUmer 많은 bluebirdjs.com/docs/api/promise.promisifyall.html
네이트

MongoDB 드라이버가 약속도 지원 한 지 오래되었습니다. 이를 활용하기 위해 예제를 업데이트 할 수 있습니까? .map(function(name) { return conn.collection(name).drop() })
djanowski

21

이를 수행하는 방법은 공유 카운터를 업데이트하는 콜백을 태스크에 전달하는 것입니다. 공유 카운터가 0에 도달하면 모든 작업이 완료된 것이므로 정상적인 흐름을 계속할 수 있습니다.

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);

물론 이런 종류의 코드를보다 일반적이거나 재사용 가능하게 만드는 방법은 여러 가지가 있으며, 많은 비동기 프로그래밍 라이브러리 에는 이러한 종류의 작업을 수행하는 기능이 적어도 하나는 있어야합니다.


이것은 구현하기 가장 쉽지는 않지만 외부 모듈이 필요하지 않은 답변을 보는 것이 정말 좋습니다. 감사합니다!
카운터

8

@freakish 답변을 확장하면 async는 각 방법을 제공하며 특히 귀하의 경우에 적합합니다.

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, 이것은 코드를 더 효율적이고 더 읽기 쉽게 만듭니다. 나는 제거의 자유를 얻었습니다 console.log('dropped')-원한다면 이것을 대신 사용하십시오.

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

외부 라이브러리없이이 작업을 수행합니다.

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

모든 답변은 아주 오래되었습니다. 2013 년 초부터 Mongoose는 모든 쿼리에 대해 점진적으로 약속 을 지원하기 시작 했으므로 앞으로 필요한 순서로 여러 비동기 호출을 구성하는 데 권장되는 방법이 될 것입니다.


0

deferred(다른 약속 / 연기 구현) 당신이 할 수 있습니다 :

// 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 또는 그러한 트랜스 파일러를 사용하고 async / await를 사용하는 경우 다음을 수행 할 수 있습니다.

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");
}

drop()Promise에 콜백을 전달할 수없고 반환을 기대할 수 없습니다 . 이 예제를 수정하고 제거 할 수 onDrop있습니까?
djanowski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.