Promise.allSettled where promises list is varied incrementally - javascript

Result: a, b, d, c.
Expected: a, b, c, d
const promises = []
console.log('a')
someFunc(promises)
Promise.allSettled(promises).then(() => console.log('d'))
function someFunc(promises) {
const promise = new Promise(resolve => setTimeout(() => {
console.log('b')
const promise2 = new Promise(resolve2 => setTimeout(() => {
console.log('c')
resolve2()
}, 3000))
promises.push(promise2)
resolve()
}, 3000))
promises.push(promise)
return promise
}

While it'd be possible to patch it up by not pushing to an array, but instead having each Promise chain off of each other in someFunc...
console.log('a')
someFunc().then(() => console.log('d'))
function someFunc() {
return new Promise(resolve => setTimeout(() => {
console.log('b')
new Promise(resolve2 => setTimeout(() => {
console.log('c')
resolve2()
}, 3000))
.then(resolve);
}, 3000))
}
A much more understandable version would promisify setTimeout to begin with instead of doing it every time.
const setTimeoutPromise = ms => new Promise(resolve => setTimeout(resolve, ms));
console.log('a')
someFunc().then(() => console.log('d'))
function someFunc() {
return setTimeoutPromise(3000)
.then(() => {
console.log('b');
return setTimeoutPromise(3000)
})
.then(() => {
console.log('c');
return setTimeoutPromise(3000)
});
}
Which can be further simplified with await...
const setTimeoutPromise = ms => new Promise(resolve => setTimeout(resolve, ms));
console.log('a')
someFunc().then(() => console.log('d'))
async function someFunc() {
await setTimeoutPromise(3000);
console.log('b');
await setTimeoutPromise(3000);
console.log('c');
await setTimeoutPromise(3000);
}

Solution
I know you may be confused on this question a lot. Here is the solution I found
const promises = []
const timeout = ms => {
const promise = new Promise(resolve => setTimeout(resolve, ms))
promises.push(promise)
return promise
}
const someFunc = () =>
timeout(1000).then(() => {
console.log('b')
timeout(1000).then(() => console.log('c'))
})
async function main() {
console.log('a')
someFunc()
let i = 0;
while (promises.length > i) {
i = promises.length
await Promise.allSettled(promises)
}
console.log('d')
}
main()
The problem I really want to express is that the content of the function someFunc is not determined at compile time while I need to make sure that the function leave no side effect once executed.

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...*/ });

How to update Promise.all with new array?

In my code below, I would like to execute a, b and c but then a1 since a1 is added within a. However, it doesn't look like Promise.all is updated with the new a1 added. What's the best way to do this? Is there Promise all update? I try not to do await a1 within a.
var arr = [];
async function a() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a");
arr.push(a1);
resolve(1);
}, 2000);
});
}
async function b() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve b");
resolve(2);
}, 4000);
});
}
async function c() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve c " + arr.length);
resolve(3);
}, 6000);
});
}
async function a1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a1");
resolve(11);
}, 2000);
});
}
arr = [a(), b(), c()];
(async function run() {
await Promise.all(arr);
})();
console.log('Done');
For a start, you push a1 where I think you'd want to push a1() - otherwise you're pushing a function, not a promise
But that won't change anything, because the array passed to Promise.all is (in every library I've seen) copied (using Array#slice) so any changes to the passed in array won't be "visible" to the Promise.all (internal) code
But, you can create a function that recursively calls Promise.all on the array until the length of the results is equal to the (current, new) length of the array
var recursiveAll = a => Promise.all(a).then(r => r.length == a.length ? r : recursiveAll(a));
var arr = [];
async function a() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a");
arr.push(a1());
resolve(1);
}, 200);
});
}
async function b() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve b");
resolve(2);
}, 400);
});
}
async function c() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve c " + arr.length);
resolve(3);
}, 600);
});
}
async function a1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Resolve a1");
resolve(11);
}, 200);
});
}
arr = [a(), b(), c()];
(async function run() {
let results = await recursiveAll(arr);
console.log(results);
})();
Interesting question...
You may indeed modify Promise.all() on the run by replacing a promise with another one even if the newly inserted one resolves the latest.
In the below example promise three will replace itself with a new promise which will resolve in an additional 2000ms later and Promise.all() will wait until it to resolves to include it's resolving value to the array provided to the .then() stage.
var fun = _ => new Promise((v,x) => setTimeout(v,2000,5)),
one = new Promise((v,x) => setTimeout(v,1000,1)),
two = new Promise((v,x) => setTimeout(v,2000,2)),
three = new Promise((v,x) => setTimeout(v,3000,fun())),
four = new Promise((v,x) => setTimeout(v,4000,4));
Promise.all([one,two,three,four])
.then(a => console.log(a));
Please wait for 5 seconds to see the result.

Categories

Resources