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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5010288/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 15:14:36  来源:igfitidea点击:

How to make a function wait until a callback has been called using node.js

javascriptmultithreadingcallbacknode.js

提问by Chris

I have a simplified function that looks like this:

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

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

Basically i want it to call myApi.exec, and return the response that is given in the callback lambda. However, the above code doesn't work and simply returns immediately.

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

Just for a very hackish attempt, i tried the below which didn't work, but at least you get the idea what i'm trying to achieve:

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

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

Basically, what's a good 'node.js/event driven' way of going about this? I want my function to wait until the callback gets called, then return the value that was passed to it.

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

回答by Jakob

The "good node.js /event driven" way of doing this is to not wait.

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

Like almost everything else when working with event driven systems like node, your function should accept a callback parameter that will be invoked when then computation is complete. The caller should not wait for the value to be "returned" in the normal sense, but rather send the routine that will handle the resulting value:

与使用 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
  });
}

So you dont use it like this:

所以你不要这样使用它:

var returnValue = myFunction(query);

But like this:

但像这样:

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

回答by Timo

One way to achieve this is to wrap the API call into a promise and then use awaitto wait for the result.

实现此目的的一种方法是将 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();

Output:

输出:

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

回答by Lucio M. Tato

check this: https://github.com/luciotato/waitfor-ES6

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

your code with wait.for: (requires generators, --harmony flag)

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

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

回答by vishal patel

If you don't want to use call back then you can Use "Q" module.

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

For example:

例如:

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. 

});

For more information refer this: https://github.com/kriskowal/q

有关更多信息,请参阅:https: //github.com/kriskowal/q

回答by Marquinho Peli

If you want it very simple and easy, no fancy libraries, to wait for callback functions to be executed in node, before executing some other code, is like this:

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

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

回答by Douglas Soares

Since node 4.8.0 you are able to use the feature of ES6 called generator. You may follow this articlefor deeper concepts. But basically you can use generators and promises to get this job done. I'm using bluebirdto promisify and manage the generator.

从节点 4.8.0 开始,您可以使用称为生成器的 ES6 功能。您可以按照本文了解更深入的概念。但基本上你可以使用生成器和承诺来完成这项工作。我正在使用bluebird来承诺和管理生成器。

Your code should be fine like the example below.

您的代码应该没问题,就像下面的例子。

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

回答by Albert

Note: This answer should probably not be used in production code. It's a hack and you should know about the implications.

注意:这个答案可能不应该用在生产代码中。这是一个 hack,你应该知道它的含义。

There is the uvrunmodule (updated for newer Nodejs versions here) where you can execute a single loop round of the libuv main event loop (which is the Nodejs main loop).

uvrun模块(此处针对较新的 Nodejs 版本进行了更新),您可以其中执行 libuv 主事件循环(即 Nodejs 主循环)的单个循环。

Your code would look like this:

您的代码如下所示:

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

(You might alternative use uvrun.runNoWait(). That could avoid some problems with blocking, but takes 100% CPU.)

(您可以替代使用uvrun.runNoWait()。这可以避免一些阻塞问题,但会占用 100% 的 CPU。)

Note that this approach kind of invalidates the whole purpose of Nodejs, i.e. to have everything async and non-blocking. Also, it could increase your callstack depth a lot, so you might end up with stack overflows. If you run such function recursively, you definitely will run into troubles.

请注意,这种方法使 Nodejs 的整个目的无效,即让所有内容都异步和非阻塞。此外,它可能会大大增加您的调用堆栈深度,因此您最终可能会出现堆栈溢出。如果你递归地运行这样的函数,你肯定会遇到麻烦。

See the other answers about how to redesign your code to do it "right".

请参阅有关如何重新设计代码以“正确”执行此操作的其他答案。

This solution here is probably only useful when you do testing and esp. want to have synced and serial code.

这里的解决方案可能仅在您进行测试和 esp 时才有用。想要同步和串行代码。

回答by Z0LtaR

supposing you have a function:

假设你有一个函数:

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


};

you can make use of callbacks like this:

您可以使用这样的回调:

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

回答by Z0LtaR

That defeats the purpose of non-blocking IO -- you're blocking it when it doesn't need blocking :)

这违背了非阻塞 IO 的目的——当它不需要阻塞时你正在阻塞它 :)

You should nest your callbacks instead of forcing node.js to wait, or call another callback inside the callback where you need the result of r.

你应该嵌套你的回调而不是强迫 node.js 等待,或者在你需要 .js 结果的回调中调用另一个回调r

Chances are, if you need to force blocking, you're thinking about your architecture wrong.

很有可能,如果您需要强制阻塞,那么您考虑的架构是错误的。

回答by SaiSurya

Using async and await it is lot more easy.

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

  }

}