Как сделать функцию до тех пор, пока не будет вызван обратный вызов с помощью node.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;
}
В принципе, какой хороший "node.js/event driven" способ обойти это? Я хочу, чтобы моя функция дождалась вызова callback, а затем вернет значение, которое было передано ему.
Ответы
Ответ 1
"Хороший node.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
});
Ответ 2
проверьте это:
https://github.com/luciotato/waitfor-ES6
ваш код с wait.for: (требует генераторов, --harmony flag)
function* (query) {
var r = yield wait.for( myApi.exec, 'SomeCommand');
return r;
}
Ответ 3
Если вы не хотите использовать обратный вызов, вы можете использовать модуль "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
Ответ 4
Примечание. Этот ответ, вероятно, не должен использоваться в производственном коде. Это взлом, и вы должны знать о последствиях.
Существует модуль 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% CPU.)
Обратите внимание, что этот подход делает недействительным всю цель Nodejs, т.е. иметь все асинхронные и неблокирующие. Кроме того, это может значительно увеличить глубину вызова, так что вы можете столкнуться с переполнением стека. Если вы запустите такую функцию рекурсивно, вы обязательно столкнетесь с проблемами.
См. другие ответы о том, как изменить свой код, чтобы сделать это "правильным".
Это решение, вероятно, полезно только при тестировании и особенно. хотите синхронизировать и серийный код.
Ответ 5
Это побеждает цель неблокирующего ввода-вывода - вы блокируете его, когда ему не нужно блокировать :)
Вы должны вложить свои обратные вызовы вместо принудительного ожидания node.js или вызвать другой обратный вызов внутри обратного вызова, где вам нужен результат r
.
Скорее всего, если вам нужно принудительно блокировать, вы неправильно думаете о своей архитектуре.
Ответ 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();
}
Ответ 7
Так как node 4.8.0 вы можете использовать функцию ES6, называемую генератором.
Вы можете следовать этой статье для более глубоких концепций.
Но в основном вы можете использовать генераторы и promises, чтобы выполнить эту работу.
Я использую bluebird, чтобы обещать и управлять генератором.
Ваш код должен быть таким же, как в примере ниже.
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));
Ответ 8
Предположим, что у вас есть функция:
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);
}
});
Ответ 9
Мне это работало, чтобы использовать
JSON.parse(result)['key']
на ожидаемый результат.
Это не может быть "супер общим", но в конце "JSON.parse" управлял ожиданием асинхронного вызова, а result['key']
не выполнял.
Ответ 10
exports.dbtest = function (req, res) {
db.query('SELECT * FROM users', [], res, renderDbtest);
};
function renderDbtest(result, res){
res.render('db', { result: result });
}
Вот как я это сделал, вам просто нужно передать "res" с ним, чтобы вы могли сделать позже