Как я могу сделать этот вызов для запроса в nodejs синхронным?

У меня есть функция в моем приложении nodejs с именем get_source_at. В качестве аргумента требуется uri, и его цель - вернуть исходный код из этого uri. Моя проблема в том, что я не знаю, как заставить функцию синхронно запросить вызов, а не давать ей эту функцию обратного вызова. Я хочу, чтобы поток управления остановился в течение нескольких секунд, необходимых для загрузки uri. Как я могу достичь этого?

function get_source_at(uri){
    var source;
    request({ uri:uri}, function (error, response, body) {
        console.log(body);
    });
    return source;
}

Кроме того, я читал о "событиях" и о том, как node "evented", и я должен уважать это при написании моего кода. Я рад это сделать, но у меня должен быть способ убедиться, что у меня есть исходный код из uri, прежде чем продолжить поток управления моим приложением - так что если это не делает синхронную функцию, как это можно сделать

Ответ 1

Вам следует избегать синхронных запросов. Если вы хотите что-то вроде синхронного потока управления, вы можете использовать async.

async.waterfall([
    function(callback){
        data = get_source_at(uri);
        callback(null, data);
    },
    function(data,callback){
        process(data, callback);
    },
], function (err,result) {
    console.log(result)
});

Ожидается, что process будет запущено после get_source_at.

Ответ 2

Вы можете с deasync:

function get_source_at(uri){
    var source;
    request({ uri:uri}, function (error, response, body) {
        source = body;
        console.log(body);
    });
    while(source === undefined) {
      require('deasync').runLoopOnce();
    }
    return source;
}

Ответ 3

Это лучший способ использования deasync.

var request = require("request")
var deasync = require("deasync")

var getHtml = deasync(function (url, cb) {
   var userAgent = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36"}
   request({
      url: url, 
      headers: userAgent
   },
   function (err, resp, body) {
      if (err) { cb(err, null) }
      cb(null, body)
   })
})

var title = /<title>(.*?)<\/title>/

var myTitle = getHtml("http://www.yahoo.com").match(title)[1]
console.log(myTitle)

Обратитесь к документации deasync, вы обнаружите, что можете использовать desync(function (n params, cb) {})
чтобы сделать функцию, где cb должен вернуться с помощью (err, data). Таким образом, fs.readFile() подобные функции могут быть легко завернуты с помощью функции deasync. Но для таких функций, как request, которые не возвращаются с помощью cb(err, data). Вы можете сделать свою собственную функцию (именованной или анонимной) с пользовательским форматом обратного вызова cb(err, data) так же, как я сделал в приведенном выше коде. Таким образом, вы можете заставить почти любую асинхронную функцию выполнять синхронизацию, ожидая обратного вызова cb(err, data), чтобы вернуться на другой уровень javascript (как указано в документации). Также убедитесь, что вы рассмотрели все способы выйти из функции, которую вы обертываете дезасингом с помощью обратных вызовов cb(err, data), иначе ваша программа будет блокирована.

Надеюсь, это помогает кому-то там!

Ответ 4

У меня должен быть способ убедиться, что у меня есть исходный код с uri, прежде чем продолжить поток управления моим приложением - так что если это не делает синхронную функцию, как это можно сделать?

Учитывая эту точку входа в ваше приложение:

function app(body) {
    // Doing lots of rad stuff
}

Вы удалите его, извлекая тело:

request({ uri: uri }, function (error, response, body) {
    if(err) return console.error(err);

    // Start application
    app(body);
}

Это то, к чему вам придется привыкнуть при программировании для node.js(и javascript вообще). Существуют модули потока управления, такие как async (которые я тоже рекомендую), но вы должны привыкнуть к продолжению стиля передачи, как он называл.

Ответ 5

Наличие простой функции блокировки - большое благо для интерактивного развития! Функция sync (определенная ниже) может синхронизировать любые обещания, резко сокращая количество синтаксиса, необходимого для игры с API, и изучите его. Например, здесь, как использовать его с puppeteer для безголового Chrome:

var browser = sync(puppeteer.connect({ browserWSEndpoint: "ws://some-endpoint"}));
var pages = sync(browser.pages())
pages.length
1
var page = pages[0]
sync(page.goto('https://duckduckgo.com', {waitUntil: 'networkidle2'}))
sync(page.pdf({path: 'webpage.pdf', format: 'A4'}))

Лучшая часть - каждая из этих строк может быть изменена до тех пор, пока она не сделает то, что вы хотите, без повторного запуска или повторного ввода всех предыдущих строк каждый раз, когда вы хотите ее протестировать. Это работает, потому что у вас есть прямой доступ к переменным browser и pages с верхнего уровня.

Вот как это работает:

const deasync = require("deasync");
const sync = deasync((promise, callback) => promise.then(result) => callback(null, result)));

Он использует пакет deasync, упомянутый в других ответах. deasync создает частичное приложение для анонимной функции, которое добавляет callback в качестве последнего аргумента и блокирует до тех пор, пока не будет вызван callback. callback получает условие ошибки в качестве своего первого аргумента (если оно есть), а результат как второй (если есть).

Ответ 6

Хорошо, прежде всего, чтобы сохранить этот асинхронный код, вы можете просто разместить соответствующий код внутри обратного вызова функции запроса, то есть он будет запущен после завершения запроса, но не остановит процессор от обработки других задач в вашем приложении. Если вам это нужно несколько раз, я бы посоветовал вам проверить синхронный запрос в Node.js, в котором описываются различные методы, чтобы упростить эту процедуру и обсуждать различные библиотеки потоков управления.