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

648   11  

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

    Ничего не найдено.