Reputation: 53
I have a connect()
function which returns a promise (it is actually a method in a class - not shown). I want this function to retry with a delay if the connection is not established (i.e. when an error occurs in the connection attempt), but so far I could not make it work.
I tried using the async js library and promise-retry library to no avail - the documentation is hard to understand. For clarity, socket.connect
emits a 'connect' event if a connection is established.
This is my code:
this.socket = new net.Socket();
this.client = new Modbus.client.TCP(this.socket, this.unitID);
const net = require('net');
const Modbus = require('jsmodbus');
connect() {
return new Promise((resolve, reject) => {
this.socket.connect(options);
this.socket.on('connect', () => {
logger.info('*****CONNECTION MADE*****');
//does something once connection made
resolve();
});
this.socket.on('error', (error) => {
logger.error('failed to connect');
this.disconnect();
reject();
});
})
}
Upvotes: 5
Views: 5183
Reputation: 1074148
I'd do it by making the function that does a single connection attempt (basically, renaming your connect
to tryConnect
or similar, perhaps even as a private method if you're using a new enough version of Node.js), and then having a function that calls it with the repeat and delay, something like this (see comments):
Utility function:
function delay(ms, value) {
return new Promise(resolve => setTimeout(resolve, ms, value);
}
The new connect
:
async connect() {
for (let attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
if (attempt > 0) {
// Last attempt failed, wait a moment
await delay(RETRY_DELAY_IN_MS);
}
try {
await tryConnect();
return; // It worked
} catch {
}
}
// Out of retries
throw new Error("Couldn't create connection");
}
(If you're using a slightly older Node.js, you may need to add (e)
after the catch
above. Leaving it off when you don't need it is a relatively new feature.)
Re your current implementation of what I'm calling tryConnect
, here are a few notes as comments for how I'd change it:
tryConnect() {
return new Promise((resolve, reject) => {
// Keep these local for now
const socket = new net.Socket();
const client = new Modbus.client.TCP(socket, this.unitID);
// Add handlers before calling `connect
socket.on('connect', () => {
logger.info('*****CONNECTION MADE*****');
// NOW save these to the instance and resolve the promise
this.socket = socket;
this.client = client;
resolve();
});
socket.on('error', (error) => {
logger.error('failed to connect');
// It's not connected, so no `disconnect` call here
reject();
});
socket.connect(options);
});
}
Upvotes: 0
Reputation: 125
In the function where connect is called, It can be called recursively, eg
const outerFunction = (times = 0) => {
if (times < 4) {
socket
.connect(params)
.then(() => {
// do good stuff
})
.catch(e => {
// increment the times so that it wont run forever
times++;
setTimeout(() => {
// delay for two seconds then try conecting again
outerFunction(times);
}, 2000);
});
}
};
This way your connect function is tried three times with a spacing of 2 seconds, i hope this solves your issue
Upvotes: 0
Reputation: 350137
First define a utility function for having the delay:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
Then chain a .catch
handler to the new Promise
:
.catch(() => delay(1000).then(() => this.connect()));
Of course, you should avoid an infinite series of retries. So implement some logic to definitely give up: after a fixed number of attempts, or after a certain time has passed, ...etc.
For instance, give a parameter to connect
how many attempts it should allow:
connect(attempts=3) {
Then the catch handler could be:
.catch((err) => {
if (--attempts <= 0) throw err; // give up
return delay(1000).then(() => this.connect(attempts));
});
Upvotes: 3