How to write an Asynchronous Loop using Promises? - javascript

How do I write a synchronous loop using Promises? Neither of the functions below wait for the previous loop to finish before starting...
(async ()=> {
let arr = [3,1,2,1,2];
const waitFor = ms => new Promise(r => setTimeout(r, ms));
// Using Promise.all
const firstFn = async () => { // doens't work
return Promise.all(arr.map(async (sec) => {
await waitFor(sec*1000);
console.log(`waited for ${sec} seconds`);
}));
}
await firstFn();
// Using new Promise
const secondFn = async () => {
arr.map(sec => {
new Promise(async (res, rej) => {
await waitFor(sec*1000);
console.log(`waited for ${sec} seconds`);
res();
});
});
}
await Promise.all(secondFn());
})();

map process the promises in a parallel execution. If you want in sequence use for... of , or the simple form of the for. Example:
async function something () {
const arr = [3,1,2,1,2];
for (let x = 0; x < arr.length; x++) {
const sec = arr[x];
await waitFor(sec*1000);
console.log(`waited for ${sec} seconds`);
}
}

Here's an example of an asynchronous function that takes a list of asynchronous functions and executes them sequentially. Waiting for one to finish before moving to the other.
const wait =
ms =>
new Promise
( resolve =>
setTimeout
( () => (console.log(`wait ${ms}`), resolve())
, ms
)
);
const async_chain =
async ([fn, ...fns]) =>
typeof fn === 'function'
? (await fn(), await async_chain(fns))
: undefined;
(async function main() {
await async_chain
( [ async () => wait(1000)
, async () => wait(2000)
, async () => wait(3000)
]
)
})();

You don't have to use promise for this. You can use the for..of loop for this:
for await (const sec of arr){
await waitFor(sec*1000);
console.log(`waited for ${sec} seconds`);
}
You can learn more about async for of loop here.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of

Related

JavaScript: how can I tweak this function to return all the results from the async functions in order

I have a function that takes an array of async/sync functions and invoke each of them sequentially (as opposed to in parallel) in the same order as the input is passed.
For example:
const sleep = (delay) => new Promise((r) => setTimeout(r, delay))
const fn1 = async () => {
await sleep(2000)
console.log('fn1')
return 'fn1'
}
const fn2 = async () => {
await sleep(3000)
console.log('fn2')
return 'fn2'
}
const fn3 = async () => {
await sleep(1000)
console.log('fn3')
return 'fn3'
}
const fn4 = () => {
console.log('fn4')
return 'fn4'
}
function serializeAsyncFns(fns) {
return fns.reduce(
(promise, fn) => promise.then(() => fn()),
Promise.resolve()
)
}
serializeAsyncFns([fn1, fn2, fn3, fn4])
// fn1 -> fn2 -> f3 -> f4
But now the return value of serializeAsyncFns is a promise that resolves to the return value of the last function in the input list, which is f4. Is there a way to tweak this funciton so that the returned promise resolves to the an array of values of all the functions, in order of how they got passed.
In this case it would be ['fn1', 'fn2', 'fn3', 'fn4']
Promise.all doesn't work here as it would fire all the promises in parallel.
easiest way is with a for loop and async/await
async function serializeAsyncFns(fns) {
const result = [];
for (const fn of fns) {
result.push(await fn());
}
return result;
}
If, for some reason you can't use async/await for that function, this is what I used to do before async/await was a thing
const serializeAsyncFns = fns =>
fns.reduce((promise, fn) =>
promise.then(results => Promise.resolve(fn()).then(result => [...results, result])),
Promise.resolve([])
);

Executing a statement after a fetch request

I have the following structure of code:
var cdict = new Map();
fetch("randomurl")
.then(res => res.json())
.then(data => {
for (const obj of data.result) {
// insert stuff into Map cdict
}
counter(0).then(res => {
console.log(cdict);
})
// ^ here after calling of counter i need to do stuff
});
const cbegin = 0;
const ccount = 10;
function counter(cnt) {
if (cnt < ccount) {
setTimeout( () => {
cnt++;
fetch(someurl)
.then(res => res.json())
.then(data => {
for (const obj of data.result) {
// do stuff
}
})
.catch(err => {
console.log(err);
});
counter(cnt);
}, 1000);
}
}
Here after execution of counter(0) call and all its fetch requests, I wish to execute a line console.log(cdict);
How can this be achieved? And is this proper way to call fetch requests with delay of 1 second?
Don't mix setTimeout event queue callbacks with promise-based code -
const sleep = time =>
new Promise(resolve => setTimeout(resolve, time))
async function main()
{ console.log("please wait 5 seconds...")
await sleep(5000)
console.log("thanks for waiting")
return "done"
}
main().then(console.log, console.error)
Don't write .then(res => res.json()) every time you need some JSON. Write it once and reuse it -
const fetchJSON(url, options = {}) =>
fetch(url, options).then(res => res.json())
async function main()
{ const data = await fetchJSON("path/to/data.json")
console.log("data received", data)
return ...
}
main().then(console.log, console.error)
Don't attempt to declare variables outside of Promises and modify them later. You cannot return the result of an asynchronous call. Asynchronous data needs to stay contained within the promise or you will be chasing hard-to-find bugs in your code -
async function main(urls)
{ const result = []
for (const u of urls) // for each url,
{ result.push(await getJSON(u)) // await fetch and append to result
sleep(1000) // wait 1 second
}
return result
}
const myUrls =
[ "foo/path/data.json"
, "another/something.json"
, "and/more/here.json"
]
main(urls)
.then(result => /* counter logic */)
.then(console.log, console.error)
Continue abstracting as you see fit -
// give reusable functions a name; use parameters for configurable behaviour
async function fetchAll(urls, delay = 100)
{ const result = []
for (const u of urls)
{ result.push(await getJSON(u))
sleep(delay)
}
return result
}
async function counter(urls)
{ const results = await fetchAll(urls) // use our generic fetchAll
const cdict = /* counter logic... */
return cdict
}
const myUrls =
[ "foo/path/data.json"
, "another/something.json"
, "and/more/here.json"
]
counter(urls).then(console.log, console.error)
As you can see async and await prevent nesting that occurs with the use of setTimeout or .then callbacks. If you use them correctly, your code remains flat and you can think about your code in a synchronous way.

how do I create a nameless async function - Nodejs

I was wondering if it's possible to create a nameless function with the quick functional notation on javascript. What I mean by changing this:
var lobby = ((io) => {
...
}
return {
getWhiteBoardId: (name2, time2, role2, pass2, need_id) => {
let promise = new Promise((res, rej) => {
setTimeout(() => res("Now it's done!"), 1000)
});
// wait until the promise returns us a value
let result = await promise;
}
})(io);
I then want to later be able to call this function:
whiteboardId = lobby.getWhiteBoardId(req.body.name, req.body.time, req.body.role, req.body.pass, need_id);
to have something at the beginning such as this:
var lobby = (async (io) => {
so that I can call my Promise and await
// defining io, to execute the code, you not need to write the next line
const io = {};
const lobby = ((io) => {
// some pre-existing code
return {
// you are using await in this method so add async to the method signature
getWhiteBoardId: async (...args) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(args), 1000)
});
// wait until the promise returns us a value
let result = await promise;
// ... some other code/logic as needed
// i have assumed you need to return the result
return result;
}
}
})(io);
// TEST: calling getWhiteBoardID method
lobby.getWhiteBoardId("Hi", "There").then(console.log);
// or
(async () => {
const res = await lobby.getWhiteBoardId("Hello World")
console.log(res);
})();
console.log("First!");

Async / Await JavaScript issue

shortly, I was trying to simulate async / await behavior in JavaScript but getting not expected
const urls = ['api1', 'api2', 'api3']
async function start() {
for (i = 0; i < urls.length; i++) {
result = await getFromApi(urls[i])
console.log(result)
}
}
async function getFromApi(apiUrl) {
return await new Promise((resolve, reject) => {
resolve(apiUrl)
}).then(apiUrl => apiUrl)
}
console.log('start ....')
start()
console.log('done ... ')
so the expected result should be
start ....
api1
api2
api3
done ...
but I am getting
start ....
done ...
api1
api2
api3
The function called start() needs be used with await. Also in the same time your code needs to be wrapped with async function.
Try as the following:
(async () => {
const urls = ['api1', 'api2', 'api3']
async function start() {
for (i = 0; i < urls.length; i++) {
result = await getFromApi(urls[i])
console.log(result)
}
}
async function getFromApi(apiUrl) {
return await new Promise((resolve, reject) => {
resolve(apiUrl)
}).then(apiUrl => apiUrl)
}
console.log('start ....')
await start()
console.log('done ... ')
})();
I hope this helps!
start() isn't being awaited. If this is at the top-level scope then you would probably use .then() on the returned Promise object. For example:
console.log('start ....');
start().then(() => {
console.log('done ... ');
});

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

Categories

Resources