如何使函数等待直到使用 node.js 调用回调

IT技术 javascript multithreading callback node.js
2021-01-11 04:43:17

我有一个简化的函数,如下所示:

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

基本上我希望它调用myApi.exec,并返回回调 lambda 中给出的响应。但是,上面的代码不起作用,只是立即返回。

只是为了一个非常hackish的尝试,我尝试了以下不起作用,但至少你明白我想要实现的目标:

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

基本上,什么是好的“node.js/事件驱动”方式来解决这个问题?我希望我的函数等到回调被调用,然后返回传递给它的值。

6个回答

这样做的“好的 node.js /事件驱动”方式是不等待

与使用 node 等事件驱动系统时的几乎所有其他事情一样,您的函数应该接受一个回调参数,该参数将在计算完成时被调用。调用者不应等待正常意义上的值“返回”,而是发送将处理结果值的例程:

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
});
对@nategood 评论的迟到回答:我可以想到几种方法;在这条评论中解释太多,但谷歌他们。请记住,Node 不是为了被阻塞,所以这些并不完美。将它们视为建议。无论如何,这里是:(1)使用C来实现您的功能并将其发布到NPM以便使用它。这就是sync方法的作用。(2) 使用Fibersgithub.com/laverdet/node-fibers,(3) 使用 promises,例如 Q-library,(4) 在 javascript 之上使用一个薄层,看起来阻塞,但编译为异步,像maxtaco.github.com/coffee-script
2021-03-14 04:43:17
或者更好(添加一个检查以便回调不能被调用两次):jsfiddle.net/LdaFw/1
2021-03-19 04:43:17
好的,不错。如果 myApi.exec 从未调用回调怎么办?我将如何使它在 10 秒后调用回调,并显示错误值,说明它计时了我们的时间或其他什么?
2021-03-20 04:43:17
当人们用“你不应该那样做”来回答问题时,真是令人沮丧。如果一个人想提供帮助并回答一个问题,那就是站起来做的事情。但是明确地告诉我我不应该做某事是不友好的。有人想要同步或异步调用例程的原因有上百万种。这是一个关于如何去做的问题。如果您在提供答案的同时提供有关 api 性质的有用建议,那会很有帮助,但如果您不提供答案,何必费心回复。(我想我真的应该提出自己的建议。)
2021-03-20 04:43:17
很明显,非阻塞是 node/js 中的标准,但是肯定有一些时候需要阻塞(例如在 stdin 上阻塞)。甚至节点也有“阻塞”方法(查看所有fs sync*方法)。因此,我认为这仍然是一个有效的问题。除了忙等待之外,是否有一种很好的方法可以在节点中实现阻塞?
2021-04-04 04:43:17

实现此目的的一种方法是将 API 调用包装到一个 Promise 中,然后使用它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
做得好。这正是我所需要的,因为我得到了一个使用回调的 API 函数调用,但我不知道如何“等待”它的结果。
2021-03-17 04:43:17
写的很好的例子。对于像我这样的初学者来说很容易理解。很高兴从异步/等待回调地狱中恢复
2021-03-20 04:43:17
这是一个用回调包装函数的很好的例子,所以你可以在async/await 我不经常需要它的情况下使用它,所以很难记住如何处理这种情况,我正在复制它作为我的个人笔记/参考。
2021-03-24 04:43:17

检查这个:https : //github.com/luciotato/waitfor-ES6

您使用 wait.for 的代码:(需要生成器,--harmony 标志)

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

如果您不想使用回调,则可以使用“Q”module。

例如:

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