Как заставить функцию ждать, пока обратный вызов не будет вызван с помощью узла.js
у меня есть упрощенная функция, которая выглядит так:
function(query) {
myApi.exec('SomeCommand', function(response) {
return response;
});
}
В основном я хочу, чтобы он позвонил myApi.exec, и возвращает ответ, который задан в лямбде обратного вызова. Однако, приведенный выше код не работает, и просто сразу возвращается.
просто для очень хакерской попытки я попробовал ниже, что не сработало, но по крайней мере вы понимаете, чего я пытаюсь достичь:
function(query) {
var r;
myApi.exec('SomeCommand', function(response) {
r = response;
});
while (!r) {}
return r;
}
в принципе, что узел хорошо '.JS / event driven' способ передвижения это? Я хочу, чтобы моя функция дождалась вызова обратного вызова, а затем вернула значение, которое было передано ей.
11 ответов:
"хороший узел.JS / event driven " способ сделать это -не дождетесь.
как и почти все остальное при работе с системами, управляемыми событиями, такими как 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 });
проверить это: https://github.com/luciotato/waitfor-ES6
ваш код с ждать.для: (требуется генераторы, -- флаг гармонии)
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(); }
Примечание: этот ответ, вероятно, не следует использовать в производственном коде. Это хак, и вы должны знать о последствиях.
есть uvrun модуль (обновлен для новых версий Nodejs здесь), где вы можете выполнить один цикл цикла основных событий libuv (который является основным циклом 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% процессора.)обратите внимание, что этот подход делает недействительным всю цель Nodejs, т. е. иметь все асинхронное и неблокирующее. Кроме того, это может значительно увеличить глубину стека вызовов, поэтому вы можете столкнуться с переполнением стека. Если вы запустите такую функцию рекурсивно, вы определенно столкнетесь с проблемами.
смотрите другие ответы о том, как изменить свой код, чтобы сделать это "правильно".
такое решение здесь, вероятно, только полезно, когда вы делаете тестирование и ESP. хотите иметь синхронизированный и серийный код.
начиная с узла 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));
предположим, у вас есть функция:
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); } });
мне это работало, чтобы использовать
JSON.parse(result)['key']на результат я рассчитывал. Это может быть не "супер генерал", а в конце концов "JSON.разбор " удалось дождаться асинхронного звонка, пока
result['key']нет.
один из способов добиться этого-обернуть вызов 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
Это побеждает цель неблокирующего ввода-вывода - вы блокируете его, когда он не нуждается в блокировке
:)вы должны вложить свои обратные вызовы вместо принудительного узла.JS, чтобы ждать, или вызвать другую обратного вызова внутри функции обратного вызова, когда вы нуждаетесь в результате
r.скорее всего, если вам нужно принудительно блокировать, вы думаете о своей архитектуре неправильно.
exports.dbtest = function (req, res) { db.query('SELECT * FROM users', [], res, renderDbtest); }; function renderDbtest(result, res){ res.render('db', { result: result }); }вот как я это сделал, вам просто нужно передать " res " с ним, так что вы можете сделать позже
Comments