У меня возникла небольшая проблема с возвратом значения из функции обратного вызова в 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
.
Его 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 и о том, как они работают. Помните, что это асинхронный код.
У меня возникла небольшая проблема с возвратом значения из функции обратного вызова в 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 используется, чтобы указать, что функция заканчивается здесь, но это не означает, что значение возвращается вызывающему абоненту (вызывающий уже перемещен.)
Если вы хотите, чтобы ваш код работал, не изменяя слишком много. Вы можете попробовать это решение, которое избавится от обратных вызовов и поддерживает тот же рабочий процесс кода:
Учитывая, что вы используете 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.
Посредством этого мы подождем, пока функция обратного вызова не закончится, а затем получите значение от него. Так или иначе, он решает вашу проблему.
Пример кода для 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);
}