What is the correct way to implement a retry on error/condition without using any third party modules in nodejs, please?
I'm not sure how to call the same function on the error and how to then pass the original callback/data to the newly called function?
Do I need to destroy/end the sockets?
I've tried looking for examples but have only found reference to third party modules and http.get samples which don't seem to work. How does one test this? I have attempted the below without success:
async pingApi(cb) {
let options = {
"method":"post",
"path": `/API/pingAPI?${this.auth}`, /ect do I reference this path?
}
};
let request = await http.request(options, (response) => {
let body = new Buffer(0);
response.on('data', (chunk) => {
body = Buffer.concat([body, chunk]);
});
response.on('end', function () {
if (this.complete) {
let decoded = new Buffer(body, 'base64').toString('utf8')
let json = JSON.parse(decoded);
if (json.result != 'OK') {
setTimeout(pingApi, 1000); //cant pass callback
} else {
cb(null, json.result) //works
}
}
});
})
request.end(); //does the socket need to be closed if retry is this function?
}
Any help, pointing in the right direction or criticism will be greatly appreciated as I think this is a very important learning curve for me.
Thank you in advance,
I'm not sure how to call the same function on the error and how to then pass the original callback/data to the newly called function?
I don't know for sure that everything else in your function is correct, but you can fix the recursion that you're asking about by changing this:
setTimeout(pingApi, 1000); //cant pass callback
to this:
setTimeout(() => {
this.pingApi(cb);
}, 1000);
You aren't showing the whole context here, but if pingApi()
is a method, then you also need to keep track of the this
value to you can call this.pingApi(db)
. You can preserve the value of this
by using arrow function callbacks like this:
response.on('end', () => { ... });
Other things I notice that look off here:
await http.request()
. http.request()
does not return a promise so using await
with it does not do anything useful. await
, there's then no reason for your function to be declared async
since nobody is using a returned promise from it. if (this.complete)
is meant to do. Since this is inside a regular function callback, the value of this
won't be your pingApi object. You should either save this
higher in the scope typically with const self = this
or all callbacks internally need to be arrow functions so the value of this
is preserved. try/catch
around JSON.parse()
because it can throw if the input is not perfect JSON. Do I need to destroy/end the sockets?
No, that will happen automatically after the request ends.
How does one test this?
You have to create a test route in your server that returns the error condition for the first few requests and then returns a successful response and see if your code works with that.
Here's an attempt at a code fixup (untested):
const maxRetries = 10;
pingApi(cb, cnt = 0) {
let options = {
"method":"post",
"path": `/API/pingAPI?${this.auth}`, // ect do I reference this path?
};
let request = http.request(options, (response) => {
let body = new Buffer(0);
response.on('data', (chunk) => {
body = Buffer.concat([body, chunk]);
});
response.on('end', () => {
if (this.complete) {
let decoded = new Buffer(body, 'base64').toString('utf8')
try {
let json = JSON.parse(decoded);
if (json.result != 'OK') {
if (cnt < maxRetries)
setTimeout(() => {
this.pingApi(cb, ++cnt);
}, 1000);
} else {
cb(new Error("Exceeded maxRetries with error on pingApi()"));
}
} else {
cb(null, json.result) //works
}
} catch(e) {
// illegal JSON encountered
cb(e);
}
}
});
})
request.end();
}
Remaining open questions about this code:
this.complete
doing and what this
should it be referencing? request.write()
to send the body of the POST request? I know you ask for no external modules, but my preferred way of doing this would be to use promises and to use the request-promise wrapper around http.request()
because it handles a lot of this code for you (checks response.status for you, parses JSON for you, uses promise interface, etc...). You can see how much cleaner the code is:
const rp = require('request-promise');
const maxRetries = 5;
pingApi(cnt = 0) {
let options = {
method: "post",
url: `http://somedomain.com/API/pingAPI?${this.auth}`,
json: true
};
return rp(options).then(result => {
if (result.result === "OK") {
return result;
} else {
throw "try again"; // advance to .catch handler
}
}).catch(err => {
if (cnt < maxRetries) {
return pingApi(++cnt);
} else {
throw new Error("pingApi failed after maxRetries")
}
});
}
And, then sample usage:
pingApi().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
})
your use of async/await with core node server intrigued me and I've tried to use much as possible of this new async features.
This is what I end up with: https://runkit.com/marzelin/pified-ping
const pify = require("util").promisify; const http = require("http"); const hostname = "jsonplaceholder.typicode.com"; const failEndpoint = "/todos/2"; const goodEndpoint = "/todos/4"; let options = { method: "get", path: `${failEndpoint}`, hostname }; async function ping(tries = 0) { return new Promise((res) => { const req = http.request(options, async (response) => { let body = new Buffer(0); response.on("data", (chunk) => { body = Buffer.concat([body, chunk]); }) const on = pify(response.on.bind(response)); await on("end"); let decoded = new Buffer(body, 'base64').toString('utf8') let json = JSON.parse(decoded); if (json.completed) { return res("all good"); } if (tries < 3) { console.log(`retrying ${tries + 1} time`); return res(ping(tries + 1)); } return res("failed"); }) req.on('error', (e) => { console.error(`problem with request: ${e.message}`); }); // write data to request body req.end(); }) } const status = await ping(); "status: " + status
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.