Как выполнять повторные запросы до тех пор, пока вам не удастся избежать блокировки в node?
У меня есть функция, которая принимает параметр и обратный вызов. Он должен выполнить запрос к удаленному API и получить некоторую информацию, основанную на параметре. Когда он получает информацию, ей необходимо отправить ее на обратный вызов. Теперь удаленный API иногда не может обеспечить. Мне нужно, чтобы моя функция продолжалась, пока она не справится с этим, а затем вызовет обратный вызов с правильными данными.
В настоящее время у меня есть код ниже внутри функции, но я думаю, что такие вещи, как while (!done
); не является правильным node кодом.
var history = {};
while (true) {
var done = false;
var retry = true;
var req = https.request(options, function(res) {
var acc = "";
res.on("data", function(msg) {
acc += msg.toString("utf-8");
});
res.on("end", function() {
done = true;
history = JSON.parse(acc);
if (history.success) {
retry = false;
}
});
});
req.end();
while (!done);
if (!retry) break;
}
callback(history);
Как мне это сделать правильно?
Ответы
Ответ 1
Определенно не путь - while (! done); войдет в сложный цикл и возьмет на себя все ваши процессоры.
Вместо этого вы можете сделать что-то вроде этого (непроверенные, и вы можете захотеть реализовать откат какого-то рода):
function tryUntilSuccess(options, callback) {
var req = https.request(options, function(res) {
var acc = "";
res.on("data", function(msg) {
acc += msg.toString("utf-8");
});
res.on("end", function() {
var history = JSON.parse(acc); //<== Protect this if you may not get JSON back
if (history.success) {
callback(null, history);
} else {
tryUntilSuccess(options, callback);
}
});
});
req.end();
req.on('error', function(e) {
// Decide what to do here
// if error is recoverable
// tryUntilSuccess(options, callback);
// else
// callback(e);
});
}
// Use the standard callback pattern of err in first param, success in second
tryUntilSuccess(options, function(err, resp) {
// Your code here...
});
Ответ 2
Нет необходимости заново изобретать колесо... вы можете использовать популярную библиотеку асинхронных утилит, в данном случае метод retry.
// try calling apiMethod 3 times
async.retry(3, apiMethod, function(err, result) {
// do something with the result
});
// try calling apiMethod 3 times, waiting 200 ms between each retry
async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
// do something with the result
});
асинхронная страница GitHub
async.retry документы
Ответ 3
Дмитрий нашел, что использование утилиты async - очень полезный и лучший ответ.
Этот ответ расширяет его пример до рабочей версии, которая определяет функцию apiMethod и передает ей параметр. Я собирался добавить код в качестве комментария, но отдельный ответ яснее.
const async = require('async');
const apiMethod = function(uri, callback) {
try {
// Call your api here (or whatever thing you want to do) and assign to result.
const result = ...
callback(null, result);
} catch (err) {
callback(err);
}
};
const uri = 'http://www.test.com/api';
async.retry(
{ times: 5, interval: 200 },
function (callback) { return apiMethod(uri, callback) },
function(err, result) {
if (err) {
throw err; // Error still thrown after retrying N times, so rethrow.
}
});
Повторите документацию: https://caolan.github.io/async/docs.html#retry
Обратите внимание, что альтернативой вызову apiMethod (uri, callback) в задаче является использование async.apply:
async.retry(
{times: 5, interval: 200},
async.apply(task, dir),
function(err, result) {
if (err) {
throw err; // Error still thrown after retrying N times, so rethrow.
}
});
Я надеюсь, что это обеспечивает хорошее решение для копирования/вставки котельной пластины для кого-то.
Ответ 4
Это то, что вы пытаетесь сделать?
var history = {};
function sendRequest(options, callback) {
var req = https.request(options, function (res) {
var acc = "";
res.on("data", function (msg) {
acc += msg.toString("utf-8");
});
res.on("end", function () {
history = JSON.parse(acc);
if (history.success) {
callback(history);
}
else {
sendRequest(options, callback);
}
});
});
req.end();
}
sendRequest(options, callback);
Ответ 5
Я решил эту проблему, используя модуль retry.
Пример:
var retry = require('retry');
// configuration
var operation = retry.operation({
retries: 2, // try 1 time and retry 2 times if needed, total = 3
minTimeout: 1 * 1000, // the number of milliseconds before starting the first retry
maxTimeout: 3 * 1000 // the maximum number of milliseconds between two retries
});
// your unreliable task
var task = function(input, callback) {
Math.random() > 0.5
? callback(null, 'ok') // success
: callback(new Error()); // error
}
// define a function that wraps our unreliable task into a fault tolerant task
function faultTolerantTask(input, callback) {
operation.attempt(function(currentAttempt) {
task(input, function(err, result) {
console.log('Current attempt: ' + currentAttempt);
if (operation.retry(err)) { // retry if needed
return;
}
callback(err ? operation.mainError() : null, result);
});
});
}
// test
faultTolerantTask('some input', function(err, result) {
console.log(err, result);
});
Ответ 6
Вы можете попробовать что-то в следующих строках. Я пишу общую идею, вы должны заменить trySomething своим HTTP-запросом.
function keepTrying(onSuccess) {
function trySomething(onSuccess, onError) {
if (Date.now() % 7 === 0) {
process.nextTick(onSuccess);
} else {
process.nextTick(onError);
}
}
trySomething(onSuccess, function () {
console.log('Failed, retrying...');
keepTrying(onSuccess);
});
}
keepTrying(function () {
console.log('Succeeded!');
});
Надеюсь, это поможет.
Ответ 7
Библиотека, называемая Flashheart, также является подходящей альтернативой. Это клиент для отдыха, который прост в использовании и поддерживает повторные попытки.
Например, настройте Flashheart на повторную попытку 10 раз с задержкой в 500 мс между запросами:
const client = require('flashheart').createClient({
retries: 10,
retryTimeout: 500
});
const url = "https://www.example.com/";
client.get(url, (err, body) => {
if (err) {
console.error('handle error: ', err);
return;
}
console.log(body);
});
Для получения дополнительной информации ознакомьтесь с документами:
https://github.com/bbc/flashheart
Отказ от ответственности: я внес вклад в эту библиотеку.