stop promises execution on certain time - javascript

function sleep(t) {
return new Promise((resolve, reject) =>{
setTimeout(() => {
console.log('timeout!')
return resolve({isTimeout: true})
}, t);
});
}
function thirdPartyFunction(t) { // thirdPartyFunction can't be edited
return new Promise((resolve, reject) =>{
setTimeout(() => {
console.log('thirdPartyFunction completed!')
return resolve({success: true})
}, t);
});
}
function main() {
return new Promise(async(resolve, reject) => {
try {
let thirdPartyFunctionExecutionTime =  Math.floor(Math.random() * 10) + 1;
thirdPartyFunction(thirdPartyFunctionExecutionTime * 1000, false).then( (r) => {
console.log('should not execute this if thirdPartyFunctionExecutionTime > timeout') // only goal
// other code which is not useful after timeout
});
const timeout = 3;
console.log(`thirdPartyFunctionExecutionTime: ${thirdPartyFunctionExecutionTime}, timeout - ${timeout}`)
await sleep(timeout * 1000, true);
throw 'stop main()'
// return
} catch (error) {
console.log('in catch')
return;
}
})
}
main()
Timeout is fixed. thirdPartyFunctionExecutionTime might be very large (sometimes) in my actual case, say 30 secs. I don't want something to be running on background after timeout.
thirdPartyFunction promise function should stop execution on timeout.

FYI, here's a generic function to add a timeout with the option of cancelling. This can be used with any promise to add a timeout to it.
If the underlying asynchronous operation represented by the promise is cancellable, then you can supply a cancel function. If the async operation finishes first, this will clean up the timer for you automatically so it doesn't keep running.
// the main purpose of this class is so that callers can test
// to see if the reject reason is a TimeoutError vs. some other type of error
class TimeoutError extends Error {
constructor(...args) {
super(...args);
}
}
// add a timeout to any promise
function addTimeout(promise, t, timeoutMsg = "timeout") {
let timer;
const timerPromise = new Promise((resolve, reject) => {
timer = setTimeout(() => {
timer = null;
// if the promise has a .cancel() method, then call it
if (typeof promise.cancel === "function") {
try {
promise.cancel();
} catch(e) {
console.log(e);
}
}
reject(new TimeoutError(timeoutMsg));
}, t)
});
// make sure the timer doesn't keep running if the promise finished first
promise.finally(() => {
if (timer) {
clearTimeout(timer);
}
})
return Promise.race([promise, timerPromise]);
}
And, you could then use this in your code like this:
function main() {
addTimeout(thirdPartyFunction(...), 3000).then(result => {
// thirdPartyFunction succeeded and timeout was not hit
// use the result here
}).catch(err => {
console.log(err);
// an error occurred here
if (err instanceof TimeoutError) {
// timeout
} else {
// other error
}
})
}
If your asynchronous operation has an ability to be cancelled if this operation times out, you could support that like this:
function main() {
let p = thirdPartyFunction(...);
p.cancel = function () {
// cancel the thirdPartyFunction here (depends uponn the specific operation)
}
addTimeout(p, 3000).then(result => {
// thirdPartyFunction succeeded and timeout was not hit
// use the result here
}).catch(err => {
console.log(err);
// an error occurred here
if (err instanceof TimeoutError) {
// timeout
} else {
// other error
}
})
}

Related

async await is not working and console not returning an error

I am trying to make an API call with async await but it's not working and not giving errors
here I am trying to call getRace() which returns a promise so I am using await keyword here:
async function RaceInfo(){
await getRace(raceID)
}
and then I call RaceInfo() and use then to handle the response:
RaceInfo().then(res=>{
if(race.status === "in-progress") {
renderAt('#leaderBoard', raceProgress(res.positions))
}else if(race.status === "finished"){
clearInterval(raceInterval) // to stop the interval from repeating
renderAt('#race', resultsView(res.positions)) // to render the results view
}
and then all of them need to be grouped inside a final promise to be called externally:
function runRace(raceID) {
return new Promise(resolve => {
// TODO - use Javascript's built in setInterval method to get race info every 500ms
async function RaceInfo(){
await getRace(raceID)
}
RaceInfo().then(res=>{
if(race.status === "in-progress") {
renderAt('#leaderBoard', raceProgress(res.positions))
}else if(race.status === "finished"){
clearInterval(raceInterval) // to stop the interval from repeating
renderAt('#race', resultsView(res.positions)) // to render the results view
resolve(res)
}
}).catch((err) => {
console.log(err);
})
const raceInterval=setInterval(RaceInfo, 500);
})
}
console is not returning errors, what could be the issue?
You don't return anything from RaceInfo, and race is undefined in callback passed to RaceInfo().then(...)...
function getRace(raceID) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({status: "finished"}), 300);
})
}
function runRace(raceID) {
return new Promise(resolve => {
// TODO - use Javascript's built in setInterval method to get race info every 500ms
async function RaceInfo() {
// Added return statement
return await getRace(raceID);
}
RaceInfo().then(race => { // Changed paramter name
if(race.status === "in-progress") {
console.log("race in progress");
// renderAt('#leaderBoard', raceProgress(res.positions))
} else if (race.status === "finished") {
console.log("race in progress");
// clearInterval(raceInterval) // to stop the interval from repeating
// renderAt('#race', resultsView(res.positions)) // to render the results view
// resolve(res)
}
}).catch((err) => {
console.log(err);
})
const raceInterval=setInterval(RaceInfo, 500);
});
}
runRace(7);

Skip waiting if async function (Promise) is taking too much time [duplicate]

This question already has answers here:
Timeout in async/await
(3 answers)
Closed 1 year ago.
In my express application, I am making call to 2 APIs. The 2nd API is managed by 3rd party and sometimes can take more than 5 seconds to respond. Hence, I want to just wait for 1 second for the API to respond. If it does not, just proceed with data from 1st API.
Below is the mock-up of the functions being called.
I am thinking to use setTimeout to throw error if the API takes more than 1 second. If the API responds within 1 second then I just cancel the setTimeout and no error is ever thrown.
But there is problem with this approach:
setTimeout errors cannot be catched using try...catch block.
I cannot use axios's timeout option, as I still need to wait for the 2nd API to finish the processing and save the data in the DB. This will ofcourse, can happen later, when the 2nd API call finishes.
// Function to simulate it's taking time.
async function cWait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Track whether it took time.
let isTimeOut = false
async function test() {
console.log('starting')
try {
const one = await apiCall1()
const myt = setTimeout(() => {
console.log('Its taking time, skip the 2nd API Call')
isTimeOut = true
throw new Error('Its taking time')
})
const two = await apiCall2(myt)
} catch (error) {
console.log(error)
}
saveInDB({ ...one, ...two })
}
async function apiCall2(timeOutInstance) {
console.log('start-apiCall')
await cWait(1800)
clearTimeout(timeOutInstance)
if (isTimeOut) saveInDB()
console.log('done-apiCall')
}
async function apiCall1() {
await cWait(5)
}
async function saveInDB(data) {
console.log('saveInDB')
}
test()
please note, this is not the answer as it was when it was accepted
as I misread the question and failed to call saveInDB in a timed out
situation
Promise.race seems perfect for the job
Also, you'd actually use your cWait function, not for mock-up, but to actually do something useful ... win the race :p
const api2delay = 800;
async function cWait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const TIMEDOUT = Symbol('TIMEDOUT');
async function cReject(ms) {
return new Promise((_, reject) => setTimeout(reject, ms, TIMEDOUT));
}
function apiCall2timeout(timeoutCallback) {
const resultApi2 = apiCall2();
const timeout = cReject(1000);
return Promise.race([resultApi2, timeout])
.catch(e => {
if (e === TIMEDOUT) {
resultApi2.then(timeoutCallback);
} else {
throw e;
}
});
}
async function test() {
console.log('starting')
let one, two;
try {
one = await apiCall1();
two = await apiCall2timeout(saveInDB);
} catch (error) {
console.log('error', error)
}
saveInDB({
...one,
...two
})
}
async function apiCall2() {
console.log('start-apiCall2')
await cWait(api2delay)
console.log('done-apiCall2')
return {
api2: 'done'
}
}
async function apiCall1() {
await cWait(5)
return {
api1: 'done'
}
}
async function saveInDB(data) {
console.log('saveInDB', data)
}
test()
Note: I changed where one and two were declared since const is block scoped
I you run with await cWait(800) in apiCall2, the saveInDB will run with both data.
But if you run await cWait(1800), the saveInDB will run 2 times.
// Function to simulate it's taking time.
async function cWait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// https://italonascimento.github.io/applying-a-timeout-to-your-promises/
const promiseTimeout = function (ms, promise) {
// Create a promise that rejects in <ms> milliseconds
let timeout = new Promise((resolve, reject) => {
let id = setTimeout(() => {
clearTimeout(id);
reject('Timed out in ' + ms + 'ms.')
}, ms)
})
// Returns a race between our timeout and the passed in promise
return Promise.race([
promise,
timeout
])
}
// Track whether it took time.
let isTimeOut = false
async function test() {
console.log('starting')
const one = await apiCall1() // get data from 1st API
let two = {};
try {
two = await promiseTimeout(1000, apiCall2())
} catch (error) {
isTimeOut = true;
console.log(error)
}
saveInDB({ ...one, ...two })
}
async function apiCall2() {
console.log('start-apiCall')
await cWait(800)
console.log('done-apiCall', isTimeOut)
if (isTimeOut) {
saveInDB({ 2: 'two' })
}
return { 2: 'two' }
}
async function apiCall1() {
await cWait(5)
return { 1: 'one' }
}
async function saveInDB(data) {
console.log('saveInDB', data)
}
test()

Async Job queue creator which works one job at the time

I need to write a JavaScript module that exports a function createJobQueue, which creates an asynchronous job queue.
This job queue should have 3 methods:
addJob
cancelJob
processAllJob
addJob adds a job to the end of the queue. It should return a promise that resolves with the value returned by job, whenever job ends up getting executed. (But addJob itself should not trigger execution of any jobs.) If job throws an error, then the promise returned by addJob should be rejected.
cancelJob removes a job from the queue. This should reject the promise returned by addJob. If no matching job is found, it does nothing.
When processAllJobs is called, the queue should process jobs (by invoking them) one-at-a-time in FIFO order until there are none left, then resolve with the number of jobs successfully processed (i.e., that did not reject or throw an error).
If any job cannot be processed (because job rejects or throws an error when invoked) the promise returned by addJob should be rejected. However, this should not stop processAllJobs from processing the rest of the queue.
What I tried so far
addJob is working good but I can't make cancelJob to reject job added by addJob and I can't make proccessAllJobs work one-at-a-time.
Here's the code:
function createJobQueue() {
return new class jobQueue{
constructor() {
this.jobs = [];
}
addJob(job) {
return new Promise((resolve, reject) => {
if (job()) {
this.jobs.push(job);
//console.log(this.jobs)
resolve(job());
}
throw new Error;
reject();
})
}
cancelJob(job) {
return new Promise(resolve => {
const index = this.jobs.findIndex(j => j === job);
if (index === -1) {
resolve();
} else {
this.jobs = this.jobs.filter((job, i) => i !== index);
}
})
}
processAllJobs() {
return new Promise(resolve => {
let count = 0;
this.jobs.forEach((f, index) => {
if(index === this.jobs.length-1) {
f()
.then(() => {
++count
})
.catch((e) => {
resolve(count);
})
}
//console.log("count", count);
f()
.then(() => {
count++
})
.catch()
})
resolve(count);
});
}
getJobs() {
console.log(this.jobs, "Next only jobs")
console.log(this.jobs.forEach(f => f().then(console.log)));
}
}
}
const queue = createJobQueue();
queue.addJob(function() {
return Promise.resolve("One")
});
queue.addJob(function() {
return Promise.resolve("Two")
});
queue.addJob(function() {
return Promise.resolve("Three")
});
//queue.getJobs();
console.log(queue.processAllJobs());
module.exports = { createJobQueue };
Your implementation of addJobs goes against the requirements: it does execute the job, while it shouldn't. Realise that the promise constructor callback is executed immediately (synchronously).
Secondly, the trouble you have in sequencing the job execution can be more easily avoided by using async/await.
I would also suggest creating the job queue as a Map keyed by job: that way you can easily find an entry by a given job.
Finally, as you need to be able to resolve/reject a job's promise at some later time, when processAllJobs is called, you will need a reference to the appropriate resolve/reject functions. You could store those in the job queue: one pair per job.
Here is how it could look:
class JobQueue{
constructor() {
this.jobs = new Map; // so we can key by job.
}
addJob(job) {
return new Promise((resolve, reject) =>
this.jobs.set(job, {resolve, reject})
);
}
cancelJob(job) {
let deferred = this.jobs.get(job);
if (this.jobs.delete(job)) deferred.reject("job cancelled");
}
async processAllJobs() {
let count = 0;
for (let [job, {resolve, reject}] of this.jobs) {
try {
resolve(await job());
count++;
} catch(e) {
reject(e);
}
}
return count;
}
}
function createJobQueue() {
return new JobQueue;
}
// Demo
const queue = createJobQueue();
// Instead of Promise.resolve, use a bit more flexible
// promise-generation function:
const delay = (ms, value, err) => new Promise((resolve, reject) =>
setTimeout(() => {
if (err) reject(err); else resolve(value);
console.log(err || value);
}, ms)
);
queue.addJob(() => delay(500, "One"));
queue.addJob(() => delay(500, "Two"));
// add an example of a rejecting promise:
queue.addJob(() => delay(500, null, "Failing job"));
queue.addJob(() => delay(500, "Three"));
queue.processAllJobs().then(count =>
console.log("count:", count)
);

Create a promise that timesout if callback never fired

I am curious how you are supposed to set a timeout for a function that returns a callback, if the callback is never fired, the promise will never resolve.
const mockCliAsync = (argv, stdio, timeout) => {
return new Promise((resolve, reject) => {
let timedout = false
const timeoutTimer = setTimeout(() => {
timedout = true
kill()
return reject(new Error('timeout'))
}, timeout)
const kill = mockCli(argv, stdio, (error, result) => {
if (!timedout) {
clearTimeout(timeoutTimer)
if (error) return reject(error)
return resolve(result)
}
})
})
}

Does promise resolved in n-th setTimeout cause memory leak?

I can see in Chrome task manager that the tab in which following code is running eats more and more memory, and it is not released until the promise is resolved
UPDATE
Main idea here is to use a single 'low level' method which would handle "busy" responses from the server. Other methods just pass url path with request data to it and awaiting for a valuable response.
Some anti-patterns was removed.
var counter = 1
// emulates post requests sent with ... axios
async function post (path, data) {
let response = (counter++ < 1000) ? { busy: true } : { balance: 3000 }
return Promise.resolve(response)
}
async function _call (path, data, resolve) {
let response = await post()
if (response.busy) {
setTimeout(() => {
_call(path, data, resolve)
}, 10)
throw new Error('busy')
}
resolve(response.balance)
}
async function makePayment (amount) {
return new Promise((resolve, reject) => {
_call('/payment/create', {amount}, resolve)
})
}
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
makePayment(500)
.then(() => {
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))
})
The first time you call _call() in here:
async function getBalance () {
return new Promise((resolve, reject) => {
_call('/balance', null, resolve)
})
}
It will not call the resolve callback and it will return a rejected promise and thus the new Promise() you have in getBalance() will just do nothing initially. Remember, since _call is marked async, when you throw, that is caught and turned into a rejected promise.
When the timer fires, it will call resolve() and that will resolve the getBalance() promise, but it will not have a value and thus you don't get your balance. By the time you do eventually call resolve(response.balance), you've already called that resolve() function so the promise it belongs to is latched and won't change its value.
As others have said, there are all sorts of things wrong with this code (lots of anti-patterns). Here's a simplified version that works when I run it in node.js or in the snippet here in the answer:
function delay(t, val) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, val), t);
});
}
var counter = 1;
function post() {
console.log(`counter = ${counter}`);
// modified counter value to 100 for demo purposes here
return (counter++ < 100) ? { busy: true } : { balance: 3000 };
}
function getBalance () {
async function _call() {
let response = post();
if (response.busy) {
// delay, then chain next call
await delay(10);
return _call();
} else {
return response.balance;
}
}
// start the whole process
return _call();
}
getBalance()
.then(balance => console.log('balance: ', balance))
.catch(e => console.error('some err: ', e))

Categories