Синхронные запросы в 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 */
}
Ответы
Ответ 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();