Reputation: 11405
Consider that we want to perform some operation which takes some time to complete in Node.js, say doHeavyWork()
. What we usually do is that we pass a callback function like that:
doHeavyWork(params, callbackFunction);
Now, I want to stablish one limit time for the operation to complete. What I want is that if the uptime of that operation exceeds the limit time, the function terminates and informs the callback that the limit time was exceeded.
How can this be accomplished in Node.js?
Upvotes: 2
Views: 718
Reputation: 135406
You'd think this is possible with a simple callback, but I'm going to illustrate a problem for you
function doHeavyWork(params, callback) {
// set the timeout
var timeout = setTimeout(callback, 5000, Error('timeout'))
// some debug message
console.log('Please wait...')
// begin heavy work
// we sill simulate heavy work with setTimeout here
setTimeout(function() {
// if the function completed in time, clear the timeout
clearTimeout(timeout)
// callback without error
callback(null, 'done')
}, 3000)
}
// check to see it work
doHeavyWork('some params', function(err, res) {
if (err)
throw error
else
console.log(res)
})
Now watch what happens if you change the 3000
to a value that exceeds the 5000
ms timeout — for example, try 10000
. You will see that the callback
is called twice. This is not good! And not trivial to fix unless you want to clutter up your function with a ton of rubbish.
If we use a Promise, we can fix this quite easily tho
function doHeavyWork(params) {
return new Promise(function(resolve, reject) {
// set the timeout
var timeout = setTimeout(reject, 5000, Error('timeout'))
// some debug message
console.log('Please wait...')
// begin heavy work
// we sill simulate heavy work with setTimeout here
setTimeout(function() {
// if the function completed in time, clear the timeout
clearTimeout(timeout)
// callback without error
resolve('done')
}, 3000)
})
}
// check to see it work
doHeavyWork('some params').then(
function(res) { console.log(res) },
function(err) { console.log(err.message) }
)
Now watch what happens if you change the 3000
to 10000
again. The Promise "callback" will only be called once
Lastly, this code can even be improved just a little bit more using Promise.race
. Promise.race
will return a new Promise that resolves or rejects as soon as one of the input Promises resolves or rejects. This is perfect for our use case.
Most crucially, notice how the timeout logic is not coupled with doHeavyWork
. This keeps your functions clean and nicely organized.
function doHeavyWork(params) {
return new Promise(function(resolve, reject) {
// some debug message
console.log('Please wait...')
// begin heavy work
// we sill simulate heavy work with setTimeout here
setTimeout(resolve, 3000, 'done')
})
}
function timeout(ms) {
return new Promise(function(resolve, reject) {
setTimeout(reject, ms, Error('timeout'))
})
}
// using Promise.race
Promise.race([
timeout(5000),
doHeavyWork('some params')
]).then(
function(res) { console.log(res) },
function(err) { console.log(err.message) }
)
In the code example above, change the timeout value of 5000
to something shorter like 1000
to see what the error looks like
Upvotes: 3