Using a setTimeout in a async function [duplicate] - javascript

This question already has answers here:
Combination of async function + await + setTimeout
(17 answers)
Closed 7 months ago.
I have a async function that waits for an axios call to complete before proceeding. The problem is that I need to put a timeout on the axios call to half a second so that I don't hit the shopify API call limit.
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await axios.get(item).then((res) => {
for (key in res.data.metafields) {
if (res.data.metafields[key].value === schoolName) {
id_for_each_student.push(shopifyAdmin + "/customers/" + res.data.metafields[key].owner_id + "/metafields.json")
}
}
})
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}
when I try putting a setTimeout, it calls the setTimeout and moves on before completing the axios calls.
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await setTimeout(function(item) {
axios.get(item).then((res) => {
for (key in res.data.metafields) {
if (res.data.metafields[key].value === schoolName) {
id_for_each_student.push(shopifyAdmin + "/customers/" + res.data.metafields[key].owner_id + "/metafields.json")
}
}
})
}, 500)
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}
Any help?

await only works on promises.
You need to wrap setTimeout in a promise:
const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay));
await waitFor(500);

setTimeout() doesn't return a Promise, but you can wrap it in one like this. I also cleaned up the rest of your code a little.
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await new Promise(resolve => {
setTimeout(resolve, 500)
})
await axios.get(item).then((res) => {
Object.values(res.data.metafields).filter(
({ value }) => value === schoolName
).forEach(({ owner_id }) => {
id_for_each_student.push(`${shopifyAdmin}/customers/${owner_id}/metafields.json`)
})
})
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}

setTimeout does not return a promise so cannot be awaited.
You could create your own promise-based setTimeout and use that.
const setTimeoutPromise = timeout => new Promise(resolve => {
setTimeout(resolve, timeout);
});
await setTimeoutPromise(500);

Create a sleep function that returns a promise that you can use, like so:
const sleep = (milliseconds=500) => new Promise(resolve => setTimeout(resolve, milliseconds))
And to use it in an async function:
(async () => {
console.log("function invoked...")
await sleep(500)
console.log("I got here about 500 milliseconds later")
})()

You need to to create new promise for example like that
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms))
}
And then use it in your code before calling API
...
await delay(500)
await axios.get(item).then((res) => {
...

I created setTimeout2 function that works the same just as a promise:
const setTimeout2 = (callback, ms) => {
return new Promise(resolve => setTimeout(() => {
callback();
resolve();
}, ms));
}
So, altogether (noticed the setTimeout2 change):
async function processMatchingSchools(customer_metafield_url) {
for (const item of customer_metafield_url) {
await setTimeout2(function(item) {
axios.get(item).then((res) => {
for (key in res.data.metafields) {
if (res.data.metafields[key].value === schoolName) {
id_for_each_student.push(shopifyAdmin + "/customers/" + res.data.metafields[key].owner_id + "/metafields.json")
}
}
})
}, 500)
}
console.log("Customer metafields to search", id_for_each_student)
processOwnerIds(id_for_each_student)
}

Related

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 set a time limit to a method in NodeJs?

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'));

async/await function does not wait for setTimeout to finish

I'm using await within an async function execute functions in a particular order, if you see here - I wanted startAnim to wait until hideMoveUI had finished executing to execute itself.
Though my console log returns:
startAnim
hideMoveUI
My code:
async function printAll() {
await hideMoveUI();
await startAnim();
}
printAll();
hideMoveUI = () => {
setTimeout(() => {
console.log('hideMoveUI');
}, 3000);
}
startAnim =() => {
setTimeout(() => {
console.log('startAnim');
}, 500);
}
Is setTimeout an async function?
How can I make the second function wait for the first one to finish? any help or advice is appreciated. Thank you in advance.
Two issues:
Your hideMoveUI/startAnim functions have no return value, so calling them results in undefined. await undefined is undefined.
If you fix #1, await would be waiting on a timer handle, which on browsers is a number. There's no way for await to know that number is a timer handle.
Instead, give yourself a promise-enabled setTimeout and use it.
E.g.:
const wait = (delay, ...args) => new Promise(resolve => setTimeout(resolve, delay, ...args));
const hideMoveUI = () => {
return wait(3000).then(() => console.log('hideMoveUI'));
};
const startAnim = () => {
return wait(500).then(() => console.log('startAnim'));
};
async function printAll() {
await hideMoveUI();
await startAnim();
}
printAll()
.catch(e => { /*...handle error...*/ });
or of course
const wait = (delay, ...args) => new Promise(resolve => setTimeout(resolve, delay, ...args));
const hideMoveUI = async () => {
await wait(3000);
console.log('hideMoveUI');
};
const startAnim = async () => {
await wait(500);
console.log('startAnim');
};
async function printAll() {
await hideMoveUI();
await startAnim();
}
printAll()
.catch(e => { /*...handle error...*/ });

One Promise calling an unknown (dynamic) number of async methods in sequence [duplicate]

This question already has answers here:
ES6 promises with timeout interval
(6 answers)
Closed 5 years ago.
I want to create one Promise that, internally, calls a series of asynchronous methods in sequence and returns the results of these methods, concatenated in an ordered Array, once the last method returns.
I was trying this:
const getArray = function (thisArray) {
return new Promise ( function (resolve, reject) {
if (thisArray.length < 3) {
setTimeout(function() {
console.log('adding element');
thisArray.push(thisArray.length);
getArray(thisArray);
}, 1000);
} else {
console.log(thisArray);
console.log('resolving');
resolve(thisArray);
}
});
}
getArray([]).then(function(data) {
console.log('thened');
console.log(data);
})
but it doesn't ever calls the then(). How can I do this?
You're not far off at all, you just need to be sure to call resolve for each new Promise. If you're still building the array, you pass the resulting promise from your recursive getArray call; otherwise, you pass the array:
const getArray = function(thisArray) {
return new Promise((resolve, reject) => {
if (thisArray.length >= 3) {
// All done
resolve(thisArray);
} else {
// Do something async...
setTimeout(() => {
// ...add the result
console.log('adding element');
thisArray.push(thisArray.length);
// ...and recurse
resolve(getArray(thisArray));
}, 1000);
}
});
}
getArray([]).then(data => {
console.log('done');
console.log(data);
});
That's a bit cleaner if you separate the function doing the async work from getArray, and ensure that the function doing the async work returns a promise. For our purposes, we can just wrap setTimeout in a Promise wrapper:
const setTimeoutP = (cb, delay) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(cb());
}, delay);
});
};
then:
const getArray = thisArray => {
if (thisArray.length >= 3) {
return Promise.resolve(thisArray);
}
return setTimeoutP(() => {
console.log("adding element");
thisArray.push(thisArray.length);
});
};
Live copy:
const setTimeoutP = (cb, delay) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(cb());
}, delay);
});
};
const getArray = thisArray => {
if (thisArray.length >= 3) {
return Promise.resolve(thisArray);
}
return setTimeoutP(() => {
console.log("adding element");
thisArray.push(thisArray.length);
return getArray(thisArray);
}, 1000);
};
getArray([]).then(data => {
console.log('done');
console.log(data);
});

Categories

Resources