Возврат значения из функции обратного вызова в Node.js
У меня возникла небольшая проблема с возвратом значения из функции обратного вызова в Node.js, я постараюсь максимально упростить мою ситуацию. У меня есть фрагмент, который берет URL-адрес и удаляет этот URL-адрес и дает результат:
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
});
Я попытался обернуть его внутри функции и вернуть значение, подобное этому:
function doCall(urlToCall) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}
Потому что в моем Node.js-коде у меня есть много операторов if-else
, где будет определено значение urlToCall
, например:
if(//somecondition) {
urlToCall = //Url1;
} else if(//someother condition) {
urlToCall = //Url2;
} else {
urlToCall = //Url3;
}
Вещь - все утверждения внутри urllib.request
останутся такими же, кроме значения urlToCall
. Поэтому определенно мне нужно включить этот общий код внутри функции. Я пробовал то же самое, но в doCall
всегда вернет мне undefined
. Я пробовал вот так:
response = doCall(urlToCall);
console.log(response) //Prints undefined
Но если я печатаю значение внутри doCall()
, он отлично отпечатывается, но он всегда будет возвращать undefined
. Согласно моему исследованию, я узнал, что мы не можем вернуть значения из функций обратного вызова! (это правда)? Если да, можете ли кто-нибудь советовать мне, как справиться с этой ситуацией, поскольку я хочу предотвратить дублирование кода в каждом блоке if-else
.
Ответы
Ответ 1
Его undefined
, потому что, console.log(response)
работает до завершения doCall(urlToCall);
. Вы также должны передать функцию обратного вызова, которая выполняется, когда ваш запрос завершен.
Во-первых, ваша функция. Передайте ему обратный вызов:
function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return callback(finalData);
});
}
Сейчас:
var urlToCall = "http://myUrlToCall";
doCall(urlToCall, function(response){
// Here you have access to your variable
console.log(response);
})
@Rodrigo опубликовал хороший ресурс в комментариях. Читайте о обратных вызовах в node и о том, как они работают. Помните, что это асинхронный код.
Ответ 2
У меня возникла небольшая проблема с возвратом значения из функции обратного вызова в Node.js
Это не "небольшая проблема", на самом деле невозможно "вернуть" значение в традиционном смысле из асинхронной функции.
Так как вы не можете "вернуть значение", вы должны вызывать функцию, которая потребуется значение, когда оно у вас есть. @display_name уже ответили на ваш вопрос, но я просто хотел указать, что возврат в doCall не возвращает значение традиционным способом. Вы можете написать doCall следующим образом:
function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
// call the function that needs the value
callback(finalData);
// we are done
return;
});
}
Линия callback(finalData);
- это то, что вызывает функцию, которая требует значения, которое вы получили от функции async. Но имейте в виду, что оператор return используется, чтобы указать, что функция заканчивается здесь, но это не означает, что значение возвращается вызывающему абоненту (вызывающий уже перемещен.)
Ответ 3
Если вы хотите, чтобы ваш код работал, не изменяя слишком много. Вы можете попробовать это решение, которое избавится от обратных вызовов и поддерживает тот же рабочий процесс кода:
Учитывая, что вы используете Node.js, вы можете использовать co и co-request для достижения той же цели без проблем обратного вызова.
В принципе, вы можете сделать что-то вроде этого:
function doCall(urlToCall) {
return co(function *(){
var response = yield urllib.request(urlToCall, { wd: 'nodejs' }); // This is co-request.
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}
Затем
var response = yield doCall(urlToCall); // "yield" garuantees the callback finished.
console.log(response) // The response will not be undefined anymore.
Посредством этого мы подождем, пока функция обратного вызова не закончится, а затем получите значение от него. Так или иначе, он решает вашу проблему.
Ответ 4
Пример кода для node.js - функция async для синхронизации:
var deasync = require('deasync');
function syncFunc()
{
var ret = null;
asyncFunc(function(err, result){
ret = {err : err, result : result}
});
while((ret == null))
{
deasync.runLoopOnce();
}
return (ret.err || ret.result);
}