[javascript] node.js를 사용하여 콜백이 호출 될 때까지 함수를 대기시키는 방법

다음과 같은 단순화 된 기능이 있습니다.

function(query) {
  myApi.exec('SomeCommand', function(response) {
    return response;
  });
}

기본적으로 나는 그것을 호출 myApi.exec하고 콜백 람다에 주어진 응답을 반환하고 싶다 . 그러나 위의 코드는 작동하지 않으며 즉시 반환됩니다.

매우 hackish 시도를 위해, 나는 작동하지 않는 아래를 시도했지만 적어도 내가 달성하려고하는 아이디어를 얻습니다.

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  while (!r) {}
  return r;
}

기본적으로 이것에 대해 좋은 ‘node.js / event driven’방법은 무엇입니까? 콜백이 호출 될 때까지 함수가 기다린 다음 전달 된 값을 반환하고 싶습니다.



답변

이것을하는 “good node.js / event driven”방법은 기다리지 않는 것 입니다.

노드와 같은 이벤트 중심 시스템으로 작업 할 때 거의 모든 것과 마찬가지로 함수는 계산이 완료되면 호출되는 콜백 매개 변수를 받아 들여야합니다. 호출자는 정상적인 의미에서 값이 “반환”될 때까지 기다리지 말고 결과 값을 처리 할 루틴을 보내십시오.

function(query, callback) {
  myApi.exec('SomeCommand', function(response) {
    // other stuff here...
    // bla bla..
    callback(response); // this will "return" your value to the original caller
  });
}

따라서 다음과 같이 사용하지 마십시오.

var returnValue = myFunction(query);

그러나 이렇게 :

myFunction(query, function(returnValue) {
  // use the return value here instead of like a regular (non-evented) return value
});


답변

이를 달성하는 한 가지 방법은 API 호출을 약속으로 래핑 한 다음 await결과를 기다리는 데 사용하는 것입니다.

// let's say this is the API function with two callbacks,
// one for success and the other for error
function apiFunction(query, successCallback, errorCallback) {
    if (query == "bad query") {
        errorCallback("problem with the query");
    }
    successCallback("Your query was <" + query + ">");
}

// myFunction wraps the above API call into a Promise
// and handles the callbacks with resolve and reject
function apiFunctionWrapper(query) {
    return new Promise((resolve, reject) => {
        apiFunction(query,(successResponse) => {
            resolve(successResponse);
        }, (errorResponse) => {
            reject(errorResponse)
        });
    });
}

// now you can use await to get the result from the wrapped api function
// and you can use standard try-catch to handle the errors
async function businessLogic() {
    try {
        const result = await apiFunctionWrapper("query all users");
        console.log(result);

        // the next line will fail
        const result2 = await apiFunctionWrapper("bad query");
    } catch(error) {
        console.error("ERROR:" + error);
    }
}

// call the main function
businessLogic();

산출:

Your query was <query all users>
ERROR:problem with the query


답변

이것을 확인하십시오 :
https://github.com/luciotato/waitfor-ES6

wait.for :를 사용하여 코드 생성 (제너레이터, –harmony 플래그 필요)

function* (query) {
  var r = yield wait.for( myApi.exec, 'SomeCommand');
  return r;
}


답변

콜백을 사용하지 않으려면 “Q”모듈을 사용하십시오.

예를 들면 다음과 같습니다.

function getdb() {
    var deferred = Q.defer();
    MongoClient.connect(databaseUrl, function(err, db) {
        if (err) {
            console.log("Problem connecting database");
            deferred.reject(new Error(err));
        } else {
            var collection = db.collection("url");
            deferred.resolve(collection);
        }
    });
    return deferred.promise;
}


getdb().then(function(collection) {
   // This function will be called afte getdb() will be executed. 

}).fail(function(err){
    // If Error accrued. 

});

자세한 내용은 다음을 참조하십시오 : https://github.com/kriskowal/q


답변

다른 코드를 실행하기 전에 노드에서 콜백 함수가 실행될 때까지 기다리는 것이 매우 간단하고 쉬운 라이브러리는 없습니다.

//initialize a global var to control the callback state
var callbackCount = 0;
//call the function that has a callback
someObj.executeCallback(function () {
    callbackCount++;
    runOtherCode();
});
someObj2.executeCallback(function () {
    callbackCount++;
    runOtherCode();
});

//call function that has to wait
continueExec();

function continueExec() {
    //here is the trick, wait until var callbackCount is set number of callback functions
    if (callbackCount < 2) {
        setTimeout(continueExec, 1000);
        return;
    }
    //Finally, do what you need
    doSomeThing();
}


답변

참고 :이 답변은 프로덕션 코드에서 사용해서는 안됩니다. 그것은 해킹이며 그 의미에 대해 알아야합니다.

libuv 메인 이벤트 루프 (Nodejs 메인 루프)의 단일 루프 라운드를 실행할 수 있는 uvrun 모듈 ( 여기서는 최신 Nodejs 버전 용으로 업데이트 됨 )이 있습니다.

코드는 다음과 같습니다.

function(query) {
  var r;
  myApi.exec('SomeCommand', function(response) {
    r = response;
  });
  var uvrun = require("uvrun");
  while (!r)
    uvrun.runOnce();
  return r;
}

(대체로 사용할 수도 있습니다 uvrun.runNoWait(). 블로킹과 관련된 일부 문제를 피할 수 있지만 100 % CPU가 필요합니다.)

이 접근 방식은 Nodejs의 전체 목적을 무효화합니다. 즉, 모든 것이 비 동기화되고 차단되지 않습니다. 또한 콜 스택 깊이를 크게 늘릴 수 있으므로 스택 오버플로가 발생할 수 있습니다. 이러한 기능을 재귀 적으로 실행하면 문제가 발생할 수 있습니다.

코드를 “올바로”재 설계하는 방법에 대한 다른 답변을 참조하십시오.

이 솔루션은 테스트 및 esp를 수행 할 때만 유용합니다. 시리얼 코드를 동기화하고 싶습니다.


답변

노드 4.8.0부터 generator라는 ES6의 기능을 사용할 수 있습니다. 더 깊은 개념을 위해이 기사 를 따를 수 있습니다 . 그러나 기본적으로 생성기를 사용 하여이 작업을 수행 할 수 있습니다. 생성기를 약속하고 관리하기 위해 블루 버드 를 사용 하고 있습니다.

아래 예제와 같이 코드가 양호해야합니다.

const Promise = require('bluebird');

function* getResponse(query) {
  const r = yield new Promise(resolve => myApi.exec('SomeCommand', resolve);
  return r;
}

Promise.coroutine(getResponse)()
  .then(response => console.log(response));