2013-09-02 3 views
16

Ich habe eine Funktion, die einen Parameter und einen Rückruf nimmt. Es soll eine Anfrage an eine Remote-API senden und einige Informationen basierend auf dem Parameter erhalten. Wenn es die Informationen erhält, muss es an den Rückruf gesendet werden. Jetzt schlägt die Remote-API manchmal fehl. Ich brauche meine Funktion zu versuchen, bis es gelingt, es zu tun und dann den Rückruf mit den richtigen Daten aufrufen.Wie wiederhole ich Anfragen, bis man erfolgreich ist, ohne im Knoten zu blockieren?

Derzeit habe ich den folgenden Code innerhalb der Funktion, aber ich denke, dass Sachen wie während (!done); ist kein richtiger Knotencode.

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); 

Wie mache ich es richtig?

Antwort

17

Definitiv nicht der Weg zu gehen - während (! Getan); wird in eine harte Schleife gehen und alle Ihre CPU aufnehmen.

Stattdessen könnte man so etwas tun (ungetestet und Sie können eine Back-off von einer Art implementieren möchten):

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... 
}); 
+1

Ich bekomme, wie meine Schleife eine schlechte Idee ist, aber ich habe ein extrem schlechtes Gefühl über Rekursion so. Was ist, wenn es 100 Versuche braucht, um es zu bekommen? 100 Anrufe, die nach erfolgreichem Abschluss beendet werden müssen, scheinen keine gute Idee zu sein. Ich bin neu im Knoten, also bin ich wahrscheinlich falsch. –

+1

Denken Sie daran, dass hier ein Async-Anruf in der Mitte ist. Dies ist kein rekursiver Aufruf - Sie werden den Stack nicht aufbauen.Und ja - Sie können auch einen Zähler implementieren, um "n" mal zu wiederholen, bevor Sie aufgeben. – dc5

+1

Nun ja, aber die Funktion kann nicht einfach beenden, wenn sie einen Async-Aufruf enthält, der noch wartet, oder? Was, wenn Sie einige der lokalen Variablen geschlossen haben? Sie müssten am Leben bleiben. –

1

Sie könnten etwas in den folgenden Zeilen versuchen. Ich schreibe eine allgemeine Idee, Sie sollten trySomething durch Ihre HTTP-Anfrage ersetzen.

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!'); 
}); 

Ich hoffe, das hilft.

5

Ist das, was Sie zu tun versuchen?

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); 
26

Es gibt keine Notwendigkeit, das Rad neu zu erfinden ist ... können Sie eine beliebte Asynchron-Utility-Bibliothek verwenden, ‚Retry‘ Methode in diesem Fall.

async.retry(3, apiMethod, function(err, result) { 
    // do something with the result 
}); 

async GitHub page

async.retry docs

+1

Wenn das Rad quadratisch ist, dann würde ich argumentieren, dass wir es besser neu erfinden. Haben Sie versucht, die obige Bibliothek zu verwenden? Nur durch das Lesen der Dokumentation hatte ich keine Ahnung, wie in der Welt sollte ich die 'async.retry' API verwenden, weil die Dokumentation keine konkreten und umfassenden Beispiel (en) –

+8

@AbimbolaEsuruoso 26 Millionen Downloads pro Monat gibt, müssen sie sein etwas richtig machen. Und Sie können es nicht ernst meinen, die Dokumente geben deutlich an, wie Sie diese Methode einschließlich Codebeispiel verwenden! Vielleicht solltest du einfach eine Frage dazu stellen, ich würde dir gerne ein extra Verwendungsbeispiel geben. –

+0

Über Beispiele sprechen: https://github.com/caolan/async/blob/master/lib/retry.js#L44 – zeroliu

2

ich gelöst habe dieses Problem mit dem retry Modul.

Beispiel:

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); 
}); 
1

Eine Bibliothek namens Flashheart auch eine geeignete Alternative. Es handelt sich um einen Rest-Client, der benutzerfreundlich ist und Wiederholungsversuche unterstützt.

Beispielsweise konfigurieren Flashheart 10-mal zu wiederholen, mit einer Verzögerung von 500ms zwischen Anfragen:

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); 
}); 

Für weitere Informationen besuchen Sie die Dokumentation aus: https://github.com/bbc/flashheart

Haftungsausschluss: Ich habe dazu beigetragen, diese Bibliothek.

Verwandte Themen