Возвращение значения из функции обратного вызова в Node.js

38

У меня возникла небольшая проблема с возвратом значения из функции обратного вызова в 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.

  • 0
    "это правда?" - определенно да.
  • 0
    @JanDvorak, так что у меня нет другого выбора, кроме дублирования кода? ;)
Показать ещё 4 комментария
Теги:

4 ответа

74
Лучший ответ

Его 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 и о том, как они работают. Помните, что это асинхронный код.

  • 10
    Это отлично работает. Но у меня есть вопрос. в вызове функции doCall внутри этого. Как я могу вернуть переменную ответа? Если, например, я хочу получить эту переменную вне функции?
  • 2
    @Romeo Ромео тот же вопрос здесь !!
Показать ещё 1 комментарий
9

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

2

Если вы хотите, чтобы ваш код работал, не изменяя слишком много. Вы можете попробовать это решение, которое избавится от обратных вызовов и поддерживает тот же рабочий процесс кода:

Учитывая, что вы используете 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.

Посредством этого мы подождем, пока функция обратного вызова не закончится, а затем получите значение от него. Так или иначе, он решает вашу проблему.

  • 0
    но это ecma6 soultion
  • 0
    и это также влияет на мою производительность
Показать ещё 2 комментария
0

Пример кода для 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);
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню