我有一个简化的函数,看起来像这样:

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

我想让它调用myApi。Exec,返回回调lambda中给出的响应。然而,上面的代码不能工作,只是立即返回。

只是为了一个非常hack的尝试,我尝试了下面的工作,但至少你知道我想要实现什么:

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

基本上,“node.js/事件驱动”的好方法是什么?我希望我的函数等待,直到回调被调用,然后返回传递给它的值。


当前回答

现在是2020年,API可能已经有了一个基于承诺的版本,可以与await一起工作。然而,一些接口,特别是事件发射器将需要这个解决方案:

// doesn't wait
let value;
someEventEmitter.once((e) => { value = e.value; });
// waits
let value = await new Promise((resolve) => {
  someEventEmitter.once('event', (e) => { resolve(e.value); });
});

在这种特殊情况下,它将是:

let response = await new Promise((resolve) => {
  myAPI.exec('SomeCommand', (response) => { resolve(response); });
});

Await在过去的3年里一直出现在新的Node.js版本中(从7.6版本开始)。

其他回答

如果你想让它非常简单,没有花哨的库,在执行一些其他代码之前,等待回调函数在node中执行,是这样的:

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

如果你不想使用回调,那么你可以使用“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

假设你有一个函数:

var fetchPage(page, callback) {
   ....
   request(uri, function (error, response, body) {
        ....
        if (something_good) {
          callback(true, page+1);
        } else {
          callback(false);
        }
        .....
   });
};

你可以像这样使用回调函数:

fetchPage(1, x = function(next, page) {
if (next) {
    console.log("^^^ CALLBACK -->  fetchPage: " + page);
    fetchPage(page, x);
}
});

使用async和await要容易得多。

router.post('/login',async (req, res, next) => {
i = await queries.checkUser(req.body);
console.log('i: '+JSON.stringify(i));
});

//User Available Check
async function checkUser(request) {
try {
    let response = await sql.query('select * from login where email = ?', 
    [request.email]);
    return response[0];

    } catch (err) {
    console.log(err);

  }

}

从节点4.8.0开始,您可以使用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));