Подтвердить что ты не робот

Синхронные запросы в Node.js

Как я могу сделать модуль запроса в Node.js загружать вещи синхронно? Лучший совет, который я видел, - это как-то использовать обратный вызов, чтобы заставить функцию не возвращаться, пока она не будет выполнена. Я пытаюсь использовать встроенную функцию "запрос" в коде (вещи нужно обрабатывать на основе этих данных, которые нельзя поместить в обратные вызовы).

Итак, как я могу использовать обратный вызов модуля "запрос", чтобы он не возвращался, пока не закончит загрузку ресурса?

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

    /* loop */ {
         /* URL Generation */


    request( {url: base + u_ext}, function( err, res, body ) {
        var split1 = body.split("\n");
        var split2 = split1[1].split(", ");
        ucomp = split2[1];
    });

    request( {url: base + v_ext}, function( err, res, body ) {
        var split1 = body.split("\n");
        var split2 = split1[1].split(", ");
        vcomp = split2[1];
    });

    /* math which needs to be after functions get variables and before loop advances */
    }
4b9b3361

Ответ 1

В 2018 году вы можете запрограммировать "обычный" стиль, используя async и await в Node.js.

Ниже приведен пример, который оборачивает запрос обратного вызова в обещание, а затем использует await для получения разрешенного значения.

const request = require('request');

// wrap a request in an promise
function downloadPage(url) {
    return new Promise((resolve, reject) => {
        request(url, (error, response, body) => {
            if (error) reject(error);
            if (response.statusCode != 200) {
                reject('Invalid status code <' + response.statusCode + '>');
            }
            resolve(body);
        });
    });
}

// now to program the "usual" way
// all you need to do is use async functions and await
// for functions returning promises
async function myBackEndLogic() {
    try {
        const html = await downloadPage('https://microsoft.com')
        console.log('SHOULD WORK:');
        console.log(html);

        // try downloading an invalid url
        await downloadPage('http://      .com')
    } catch (error) {
        console.error('ERROR:');
        console.error(error);
    }
}

// run your async function
myBackEndLogic();

Ответ 2

Короткий ответ: не надо. (...) Ты действительно не можешь. И это хорошая вещь

Я хотел бы установить запись прямо относительно этого:

NodeJS поддерживает синхронные запросы. Он не был предназначен для поддержки их из коробки, но есть несколько обходных решений, если вы достаточно увлечены, вот пример:

var request = require('sync-request'),
    res1, res2, ucomp, vcomp;

try {
    res1 = request('GET', base + u_ext);
    res2 = request('GET', base + v_ext);
    ucomp = res1.split('\n')[1].split(', ')[1];
    vcomp = res2.split('\n')[1].split(', ')[1];
    doSomething(ucomp, vcomp);

} catch (e) {}

Когда вы открываете капот в библиотеке "sync-request", вы можете видеть, что в фоновом режиме выполняется синхронный README, его следует использовать очень разумно. Этот подход блокирует основной поток, и это плохо для производительности.

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

Это предположение по умолчанию, поддерживаемое многими библиотеками запросов HTTP на других языках (Python, Java, С# и т.д.), и эта философия также может быть перенесена на JavaScript. Язык - это инструмент для решения проблем, и иногда вы не можете использовать обратные вызовы, если преимущества перевешивают недостатки.

Для пуристов JavaScript это может быть риском ереси, но я прагматик, поэтому я могу ясно видеть, что простота использования синхронных запросов помогает, если вы обнаружите себя в следующих сценариях:

  • Автоматизация тестирования (тесты обычно являются синхронными по своей природе).

  • Quick API mash-ups (т.е. hackathon, доказательство работы концепции и т.д.).

  • Простые примеры для начинающих (до и после).

Будьте предупреждены, что приведенный выше код не должен использоваться для производства. Если вы собираетесь запускать надлежащий API, используйте обратные вызовы, используйте асинхронную библиотеку, используйте promises или что-то еще, но избегайте синхронного кода, если вы не хотите нести значительные затраты на потраченное время CPU на вашем сервере.

Ответ 3

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

Там мало или ничего, что нельзя поставить в обратный вызов. Если они зависят от общих переменных, создайте замыкание, содержащее их. Какая актуальная задача под рукой?

Вы хотели бы иметь счетчик и вызывать обратный вызов только тогда, когда есть данные:

var waiting = 2;
request( {url: base + u_ext}, function( err, res, body ) {
    var split1 = body.split("\n");
    var split2 = split1[1].split(", ");
    ucomp = split2[1];
    if(--waiting == 0) callback();
});

request( {url: base + v_ext}, function( err, res, body ) {
    var split1 = body.split("\n");
    var split2 = split1[1].split(", ");
    vcomp = split2[1];
    if(--waiting == 0) callback();
});

function callback() {
    // do math here.
}

Обновление 2018: node.js поддерживает ключевые слова async/await в последних выпусках, и с библиотеками, которые представляют асинхронные процессы в качестве обещаний, вы можете ожидать их. Вы получаете линейный, последовательный поток через вашу программу, и другая работа может продолжаться, пока вы ждете. Он довольно хорошо построен и стоит попробовать.

Ответ 4

Вы должны взглянуть на библиотеку под названием async

и попробуйте использовать async.series для вашей проблемы.

Ответ 5

Ответ Aredridels относительно хорош (упрекнул его), но я думаю, что ему не хватает эквивалента цикла. Это должно помочь вам:

эквивалент кода синхронизации:

while (condition) {
  var data = request(url);
  <math here>
}
return result;

Асинхронный код для последовательного выполнения:

function continueLoop() {
  if (!condition) return cb(result);
  request(url, function(err, res, body) {
    <math here>
    continueLoop()
  })
}
continueLoop()

Ответ 6

Хотя асинхронный стиль может быть характером node.js, и обычно вы не должны этого делать, вы хотите сделать это несколько раз.

Я пишу удобный script для проверки API и хочу не испортить его с помощью обратных вызовов.

Javascript не может выполнять синхронные запросы, но библиотеки C могут.

https://github.com/dhruvbird/http-sync

Ответ 7

Вы можете сделать то же самое с библиотекой запросов, но это синхронизируется с использованием const https = require('https'); или const http = require('http'); , который должен прийти с узла.

Вот пример,

const https = require('https');

const http_get1 = {
    host : 'www.googleapis.com',
    port : '443',
    path : '/youtube/v3/search?arg=1',
    method : 'GET',
    headers : {
        'Content-Type' : 'application/json'
    }
};

const http_get2 = {
host : 'www.googleapis.com',
    port : '443',
    path : '/youtube/v3/search?arg=2',
    method : 'GET',
    headers : {
        'Content-Type' : 'application/json'
    }
};

let data1 = '';
let data2 = '';

function master() {

    if(!data1)
        return;

    if(!data2)
        return;

    console.log(data1);
    console.log(data2);

}

const req1 = https.request(http_get1, (res) => {
    console.log(res.headers);

    res.on('data', (chunk) => {
        data1 += chunk;
    });

    res.on('end', () => {
        console.log('done');
        master();
    });
});


const req2 = https.request(http_get2, (res) => {
    console.log(res.headers);

    res.on('data', (chunk) => {
        data2 += chunk;
    });

    res.on('end', () => {
        console.log('done');
        master();
    });
});

req1.end();
req2.end();