How to set a time limit to a method in NodeJs? - javascript

I have a use case, where I am doing an external API call from my code,
The response of the external API is required by my code further on
I am bumping into a scenario, where the external API call at times takes far too long to return a response,
casing my code to break, being a serverless function
So I want to set a time limit to the external API call,
Where if I don't get any response from it within 3 secs, I wish the code to gracefully stop the further process
Following is a pseudo-code of what I am trying to do, but couldn't figure out the logic -
let test = async () => {
let externalCallResponse = '';
await setTimeout(function(){
//this call sometimes takes for ever to respond, but I want to limit just 3secs to it
externalCallResponse = await externalCall();
}, 3000);
if(externalCallResponse != ''){
return true;
}
else{
return false;
}
}
test();
Reference -
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SSM.html#getParameters-property
I'm using AWS SSM's getParameters method

You cannot await setTimeout as it doesn't returns a Promise.
You could implement a function that returns a Promise which is fulfilled after 3 seconds.
function timeout(seconds) {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000)
});
}
You can await the above function in your code passing the number of seconds you want to wait for
let test = async () => {
let externalCallResponse = '';
setTimeout(async () => {
externalCallResponse = await externalCall();
}, 0);
await timeout(3); // wait for 3 seconds
if(externalCallResponse != '') return true;
else return false;
}
Following code snippet demonstrates the usage of timeout function written above. It mocks a api request that returns a response after 4 seconds.
function timeout(seconds) {
return new Promise(resolve => {
setTimeout(resolve, seconds * 1000);
});
}
function apiRequest() {
return new Promise(resolve => {
setTimeout(() => resolve('Hello World'), 4000);
});
}
let test = async () => {
let externalCallResponse = '';
setTimeout(async () => {
externalCallResponse = await apiRequest();
}, 0);
await timeout(3); // wait for 3 seconds
if (externalCallResponse != '') return true;
else return false;
};
test()
.then(res => console.log(res))
.catch(err => console.log(err.message));

you can use something like this, I created a function that return a promise then I used this promise.
let test = async () => {
return promiseTimeout()
}
const promiseTimeout = () => {
return new Promise(async (resolve, reject) => {
setTimeout(function () {
let externalCallResponse=""
externalCallResponse = await externalCall();
if (externalCallResponse != '') {
return resolve(true);
}
else {
return resolve(false);
}
}, 3000);
})
}
test().then(result=>{
console.log(result);
});

You could do something like this:
const timeout = async (func, millis) => {
return new Promise(async (resolve, reject) => {
setTimeout(() => reject(), millis);
resolve(await func());
});
}
timeout(() => doStuff(), 3000)
.then(() => console.log('worked'))
.catch(() => console.log('timed out'));
Tests:
const timeout = async (func, millis) => {
return new Promise(async (resolve, reject) => {
setTimeout(() => reject(), millis);
resolve(await func());
});
}
const doStuffShort = async () => { // Runs for 1 second
return new Promise((resolve) => setTimeout(() => resolve(), 1000));
}
const doStuffLong = async () => { // Runs for 5 seconds
return new Promise((resolve) => setTimeout(() => resolve(), 5000));
}
timeout(() => doStuffShort(), 3000)
.then(() => console.log('1 worked'))
.catch(() => console.log('1 timed out'));
timeout(() => doStuffLong(), 3000)
.then(() => console.log('2 worked'))
.catch(() => console.log('2 timed out'));

Related

Fetch, then and setTimeout

How can I do setTimeout in then block?
I set setTimeout there, but program just goes through and does not wait for anything.
const controller = new AbortController();
const { signal } = controller.signal;
return fetch(url, { signal })
.then((response) => {
if (response.status === 404) {
controller.abort();
setTimeout(() => controller.abort(), 5000);
}
return response;
})
.then((response) => {
console.log('in');
setTimeout(() => controller.abort(), 5000);
return response.text();
})
.catch(() => {
return Promise.reject(new Error('empty link'));
});
I tried to set settimeout in then block, in catch block, tried to use for it the function which would return promise. The logic seems simple, but somehow I don't understand something about how to connect then block and settimeout functionality.
The setTimeout() call doesn't block. If you want to "await" for x milliseconds, you can do the following:
const timeout = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
const doSomething = async () => {
await timeout(3000);
// after 3 seconds
}

Perform an action after a promise.then callback

I'm trying to encapsulate some intialization / clean up code in a single Promise. What I want if to execute some code, execute the then and then execute some more code. This is what I came up with:
function initialize() {
let callback;
console.log('intialization');
const promise = new Promise(resolve => callback = resolve);
new Promise(async () => {
await callback();
await promise;
console.log('cleanup');
});
return promise;
}
initialize().then(() => console.log('execute then'));
which gives me the following output in the terminal:
initialization
execute then
cleanup
- Promise {<fulfilled>: undefined}
All good so far. However, when we make the callback async, it no longer works.
initialize().then(
async () => {
await new Promise(resolve => {
setTimeout(
() => {
console.log('execute then');
resolve();
},
10000
)
})
}
);
gives me this output:
initialization
cleanup
- Promise {<pending>}
execute then
I would have expected it to look like this:
initialization
- Promise {<pending>}
execute then
cleanup
How can I fix this? Is this even possible at all?
You can accept a callback that defines an asynchronous operation. Then it can be inserted into the middle of an promise chain:
const delayMessage = (message, ms) =>
new Promise(resolve => setTimeout(() => {
console.log(message);
resolve();
}, ms));
async function somethingAsync() {
console.log('intialization');
}
function initialize(callback) {
return somethingAsync()
.then(callback)
.then(() => {
console.log('cleanup');
});
}
const middleOfProcess = () => delayMessage('execute then', 2000);
initialize(middleOfProcess);
It works even if there are multiple async steps to do in between, since you can simply chain them together:
const delayMessage = (message, ms) =>
new Promise(resolve => setTimeout(() => {
console.log(message);
resolve();
}, ms));
async function somethingAsync() {
console.log('intialization');
}
function initialize(callback) {
return somethingAsync()
.then(callback)
.then(() => {
console.log('cleanup');
});
}
const middleOfProcess = () => delayMessage('execute then1', 2000)
.then(() => delayMessage('execute then2', 2000))
.then(() => delayMessage('execute then3', 2000));
initialize(middleOfProcess);
The same can be done using async/await syntax:
const delayMessage = (message, ms) =>
new Promise(resolve => setTimeout(() => {
console.log(message);
resolve();
}, ms));
async function somethingAsync() {
console.log('intialization');
}
async function initialize(callback) {
await somethingAsync();
await callback();
console.log('cleanup');
}
const middleOfProcess = async () => {
await delayMessage('execute then1', 2000);
await delayMessage('execute then2', 2000);
await delayMessage('execute then3', 2000);
};
initialize(middleOfProcess);

How to get all promises in map with timeout inside

I am trying to get all the results from a map with time out.
I’ve tried to use promise.all() But it didn’t succeed because of the setTimeout function.
I will be happy if someone can look oh my code and suggest how to do it right.
Thank you very much.
new Promise(async (resolve, reject) => {
Promise.all(
items.map(async (item, i) => {
await setTimeout(async () => {
return await SendMail(item);
}, 5000 * i);
})
).then((mailsRes) => {
resolve(mailsRes);
});
});
Simply loop through your items and sleep for x seconds (5 in this example) after sending each email.
const sleep = (milliSeconds) => {
return new Promise((resolve, _reject) => {
setTimeout(() => {
resolve()
}, milliSeconds)
})
}
const sendEmails = async (items) => {
for (let i = 0; i < items.length; i++) {
const currentItem = items[i];
await SendMail(currentItem);
await sleep(5000)
}
}
as you see sendEmails is an async function then you can call it by:
await sendEmails(items)
Not sure what you are trying to achieve but you probably need this-
async function timeout(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
});
}
new Promise( async (resolve, reject) => {
Promise.all(items.map( async (item, i)=>{
await timeout(5000 * i).then(() => {
return await SendMail(item);
});
}))
.then(mailsRes => {
resolve(mailsRes)
})
});
The problem is that setTimeout is resolved immediately and you get a timeout canceller in the next callback.
If I understand you correctly, that might do the trick:
function delayedSendMail(item, i) {
return new Promise(resolve => setTimeout(() => resolve(SendMail(item)), 5000 * i));
}
Promise.all(items.map((item, i) => delayedSendMail(item, i)))
.then(mailResults => {
// handle results
});

How to cancel JavaScript sleep? [duplicate]

This question already has answers here:
How to cancel timeout inside of Javascript Promise?
(4 answers)
Closed 3 months ago.
This function is used to wait for millis number of second.
function delay(millis: number) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, millis);
});
My goal is then to return the timeout out of this function?
const timeout = await delay(20000);
on click somewhere else, user bored of waiting
clearTimeout(timeout)
Simply extend the Promise object you'll be sending:
function delay( millis ) {
let timeout_id;
let rejector;
const prom = new Promise((resolve, reject) => {
rejector = reject;
timeout_id = setTimeout(() => {
resolve();
}, millis);
});
prom.abort = () => {
clearTimeout( timeout_id );
rejector( 'aborted' );
};
return prom;
}
const will_abort = delay( 2000 );
const will_not_abort = delay( 2000 );
will_abort
.then( () => console.log( 'will_abort ended' ) )
.catch( console.error );
will_not_abort
.then( () => console.log( 'will_not_abort ended' ) )
.catch( console.error );
setTimeout( () => will_abort.abort(), 1000 );
You can return the resolve / reject along with the promise
function delay(millis) {
let reolver;
return [new Promise((resolve, reject) => {
resolver = resolve
x = setTimeout(() => {
resolve();
}, millis);
}), resolver];
}
const [sleep1, wake1] = delay(2000)
sleep1.then((x) => console.log(x || 'wakeup 1')) // Auto wake after 2 seconds
const [sleep2, wake2] = delay(2000)
sleep2.then((x) => console.log(x || 'wakeup 2'))
wake2('Custom Wakeup') // sleep2 cancelled will wake from wake2 call
You can use an AbortSignal:
function delay(ms, signal) {
return new Promise((resolve, reject) => {
function done() {
resolve();
signal?.removeEventListener("abort", stop);
}
function stop() {
reject(this.reason);
clearTimeout(handle);
}
signal?.throwIfAborted();
const handle = setTimeout(done, ms);
signal?.addEventListener("abort", stop);
});
}
Example:
const controller = new AbortController()
delay(9000, controller.signal).then(() => {
console.log('Finished sleeping');
}, err => {
if (!controller.signal.aborted) throw err;
// alternatively:
if (err.name != "AbortError") throw err;
console.log('Cancelled sleep before it went over 9000')
});
button.onclick => () => {
controller.abort();
};
You can return a function which cancels the timer (cancelTimer) along with the Promise object as an array from the delay function call.
Then use the cancelTimer to clear it if required and which would also reject the Promise:
function delay(millis) {
let cancelTimer;
const promise = new Promise((resolve, reject) => {
const timeoutID = setTimeout(() => {
resolve("done");
}, millis);
cancelTimer = () => {
clearTimeout(timeoutID);
reject("Promise cancelled");
};
});
return [promise, cancelTimer];
}
//DEMO
let [promiseCancelled, cancelTimer] = delay(20000);
(async() => {
try {
console.log("Promise result never printed", await promiseCancelled);
} catch (error) {
console.error("Promise is rejected", error);
}
})();
cancelTimer();
const [promise, _] = delay(2000);
(async() => {
console.log("Promise result printed", await promise);
})();
I recommend that you do not modify the promise by attaching an .abort method to it. Instead return two values from your delay function
the delayed value
a function that can be called to cancel the timeout
Critically, we do not see "x" logged to the console because it was canceled -
function useDelay (x = null, ms = 1000)
{ let t
return [
new Promise(r => t = setTimeout(r, ms, x)), // 1
_ => clearTimeout(t) // 2
]
}
const [x, abortX] =
useDelay("x", 5000)
const [y, abortY] =
useDelay("y canceled x", 2000)
x.then(console.log, console.error)
y.then(console.log, console.error).finally(_ => abortX())
console.log("loading...")
// loading...
// y canceled x
interactive demo
Here's an interactive example that allows you to wait for a delayed value or abort it -
function useDelay (x = null, ms = 1000)
{ let t
return [
new Promise(r => t = setTimeout(r, ms, x)), // 1
_ => clearTimeout(t) // 2
]
}
const [ input, output ] =
document.querySelectorAll("input")
const [go, abort] =
document.querySelectorAll("button")
let loading = false
function reset ()
{ loading = false
input.value = ""
go.disabled = false
abort.disabled = true
}
function load ()
{ loading = true
go.disabled = true
abort.disabled = false
}
go.onclick = e => {
if (loading) return
load()
const [delayedValue, abortValue] =
useDelay(input.value, 3000)
abort.onclick = _ => {
abortValue()
reset()
}
delayedValue
.then(v => output.value = v)
.catch(e => output.value = e.message)
.finally(_ => reset())
}
code { color: dodgerblue; }
<input placeholder="enter a value..." />
<button>GO</button>
<button disabled>ABORT</button>
<input disabled placeholder="waiting for output..." />
<p>Click <code>GO</code> and the value will be transferred to the output in 5 seconds.</p>
<p>If you click <code>ABORT</code> the transfer will be canceled.</p>
Just use an AbortSignal:
/**
* Sleep for the specified number of milliseconds, or until the abort
* signal gets triggered.
*
* #param ms {number}
* #param signal {AbortSignal|undefined}
*/
const sleep = (ms, signal) =>
new Promise<void>((ok, ng) => {
/** #type {number} */
let timeout
const abortHandler = () => {
clearTimeout(timeout)
const aborted = new Error(`sleep aborted`)
aborted.name = 'AbortError'
ng(aborted)
}
signal?.addEventListener('abort', abortHandler)
timeout = setTimeout(() => {
signal?.removeEventListener('abort', abortHandler)
ok()
}, ms)
})
> const a = new AbortController()
undefined
> const s = sleep(900000, a.signal)
undefined
> a.abort()
undefined
> await s
Uncaught AbortError: sleep aborted
at AbortSignal.abortHandler
at innerInvokeEventListeners
at invokeEventListeners
at dispatch
at AbortSignal.dispatchEvent
at AbortSignal.[[[signalAbort]]]
at AbortController.abort
at <anonymous>:2:3
>
If you prefer not to fail the promise, i.e. treat the abort signal as "skip sleep", simply replace the call to ng(aborted) with a call to ok().

Nodejs - Retry same function on error callback in production

I have a javascript function with setTimeOut and I am retrying to call same function, if any error from API call.I am calling same function in catch block.Is my node server is going crash and resources will be blocked or it will keep calling getData() function
let retry = ()=> {
setTimeout(() => {
getData()
retry()
}, 3000);
}
let getData = () =>{
Someapi.getData().then((token) => {
console.log(`Data after 3 seconds->${token}`)
}).catch((err) => {
getData()
})
}
I do not know if this work.
let retry = () => {
setTimeout(() => {
getData();
retry();
}, 3000);
};
while (true) {
let getData = () => {
Someapi.getData()
.then(token => {
console.log(`Data after 3 seconds->${token}`);
return false;
})
.catch(err => {
return true;
});
};
}
I use this retry code in my project, it works well in production:
const pause = (duration) => {
return new Promise(resolve => setTimeout(resolve, duration));
};
const retry = (retryTimes, func, delay) => {
return func().catch(
(err) => {
if(retryTimes > 0) {
return pause(delay).then(
() => retry(retryTimes - 1, func, delay * 2)
);
} else {
return Promise.reject(err);
}
}
);
};

Categories

Resources