Async / Await JavaScript issue - javascript

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

Related

How to sleep between each axios call in react

I'm writing an app that needs to query an RestAPI with a set of input. However, that API is very bad at scaling and it will return code 429 if it receives too many request. So I was trying to add a sleep function between each axios call but it seems not working. Wondering if I have this sleep function set correctly.
const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
useEffect(() => {
let results = [];
async function fetchData(id) {
let request = someAPI + id + query;
axios.get(request).then((result) => {
results.push(result);
}).catch((error) => {console.log(error);});
await sleep(2000);
}
for (let i = 1; i <= 20; i++) {
fetchData(i);
}
},[]);
You didn't await the fetchData function, you can try this.
useEffect(() => {
let results = [];
async function fetchData(id) {
try {
const result = await axios.get('')
results.push(result);
await sleep(2000)
}
catch(error) {
console.log(error);
}
}
(async function() {
for (let i = 1; i <= 20; i++) {
await fetchData(i);
}
})()
},[]);
In your for loop, you have to await the fetchData function. This way, for each iteration, the process will wait for fetchData (and therefore the nested call to the sleep function) to complete.
Currently, you're just firing off all 20 calls to fetchData at once, without actually waiting for your sleep function.
async function fetchAll() {
for (let i = 0; i < 20; i++) {
await fetchData()
}
}
fetchAll()
Note that you will have to wrap your for-loop in an async function, in order to be able to use await within the loop.

Wait for async function to end

I have the following function that works fine, except I need to wait until it finishes to execute the next statement:
zohoAuth.zoho_oAuth = function () {
// return new Promise((resolve, reject) => {
zohoAuth.state = utils.uuid();
const url = zohoAuth.authorizationURL();
zohoAuth.popUp(url);
getAuthCodeFromCatalyst();
//setTimeout(getAuthCodeFromCatalyst,1000);
function getAuthCodeFromCatalyst() {
return new Promise(function (resolve, reject) {
(async function waitForFoo() {
const gotAuthState = await zohoAuth.getUserDataFromStorageState(zohoAuth.state)
await gotAuthState;
if (gotAuthState) return resolve();
setTimeout(waitForFoo, 1000);
})();
});
}
console.log("bottom of zoho auth")
return true;
// });
}
I call the function with this:
zohoAuth.zoho_oAuth();
console.log("done waiting");
How do i wait for this to finish?
You're making this harder on yourself. Make sure to avoid the explicit promise constructor anti-pattern -
zohoAuth.zoho_oAuth = function () {
zohoAuth.state = utils.uuid();
const url = zohoAuth.authorizationURL();
zohoAuth.popUp(url);
return zohoAuth.getUserDataFromStorageState(zohoAuth.state);
}
You can access the result by attaching a .then handler to the result of your function call -
zohoAuth.zoho_oAuth()
.then(authState => console.log("bottom of auth state", authState))
.catch(console.error)
If you want to use async and await, go ahead. If an error occurs, don't catch it. Instead allow it to bubble up and be handled by the caller -
async function doAuth (...) {
const authState = await zohoAuth.zoho_oAuth()
console.log("received auth state", authState)
return "done" // or whatever
})
doAuth().then(console.log, console.error)
You should consider awaiting on the promise. Below snippet shows the difference of using await -
const asyncFunction = function() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log('inside promise');
resolve();
}, 100);
});
}
function callWithoutAwait() {
asyncFunction();
console.log('after without await function');
}
callWithoutAwait();
async function callWithAwait() {
await asyncFunction();
console.log('after with await function');
}
callWithAwait();
I was able to accomplish what I needed below is the code. Thanks for the help!
zohoAuth.zoho_oAuth = function() {
zohoAuth.state = utils.uuid();
const url = zohoAuth.authorizationURL();
zohoAuth.popUp(url);
return new Promise(function (resolve, reject) {
(async function waitForFoo() {
const gotAuthState = await zohoAuth.getUserDataFromStorageState(zohoAuth.state)
await gotAuthState;
if (gotAuthState) return resolve();
setTimeout(waitForFoo, 1000);
})();
});
}
And this is the call:
zohoAuth.zoho_oAuth()
.then(authState => console.log("bottom of auth state", authState))
.catch(console.error)

Problem with .push with Asynchronous function

The Problem is with the uplines.push.
I always get an empty uplines array so the last part of the code doesn't run. The promises resolve later and I get the correct data. May I know how to go about doing it the correct way?
const getAllUplines = async () => {
uplines = [];
const findUser = async (userFid) => {
const userDoc = await firestore.collection("users").doc(userFid).get();
if (userDoc.exists) {
const user = { ...userDoc.data(), id: userDoc.id };
console.log(user);
uplines.push(user);
if (user.immediateUplineFid) {
findUser(user.immediateUplineFid); //self looping
}
} else {
console.log("No User Found");
return null;
}
};
sale.rens.forEach(async (ren) => {
findUser(ren.userFid);
});
console.log(uplines);
return uplines;
};
let uplines = await getAllUplines();
console.log(uplines);
uplines = uplines.filter(
(v, i) => uplines.findIndex((index) => index === v) === i
); //remove duplicates
uplines.forEach((user) => {
if (user.chatId) {
sendTelegramMessage(user.chatId, saleToDisplay, currentUser.displayName);
console.log("Telegram Message Sent to " + user.displayName);
} else {
console.log(user.displayName + " has no chatId");
}
});
There are a few things that you have missed out while implementing the async call, which are explained in the inline comments in the code snippet.
A short explanation for what happened in your code is that in the line sale.rens.forEach you are passing an async function in the argument, which does not make any difference to the function forEach, it will execute it without waiting for it to complete.
Therefore in my answer I am using Promise.all to wait for all the async function calls to complete before returning the result.
// This is wrapped in an immediately executed async function because await in root is not supported here
(async () => {
const mockGetData = () => new Promise(resolve => setTimeout(resolve, 1000));
const sale = {
rens: [
{ userFid: 1 },
{ userFid: 2 },
{ userFid: 3 }
]
};
const getAllUplines = async () => {
const uplines = [];
const findUser = async (userFid) => {
// Simulating an async function call
const userDoc = await mockGetData();
console.log("User data received");
uplines.push(`User ${userFid}`);
};
const promises = [];
sale.rens.forEach(ren => { // This function in foreach does not have to be declared as async
// The function findUser is an async function, which returns a promise, so we have to keep track of all the promises returned to be used later
promises.push(findUser(ren.userFid));
});
await Promise.all(promises);
return uplines;
};
let uplines = await getAllUplines();
console.log(uplines);
})();
In order to get the results of getAllUplines() properly, you need to add await to all async functions called in getAllUplines().
const getAllUplines = async () => {
uplines = [];
const findUser = async (userFid) => {
const userDoc = await firestore.collection("users").doc(userFid).get();
if (userDoc.exists) {
const user = { ...userDoc.data(), id: userDoc.id };
console.log(user);
uplines.push(user);
if (user.immediateUplineFid) {
await findUser(user.immediateUplineFid); //self looping
}
} else {
console.log("No User Found");
return null;
}
};
sale.rens.forEach(async (ren) => {
await findUser(ren.userFid);
});
console.log(uplines);
return uplines;
};

How to write an Asynchronous Loop using Promises?

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

Delay Mocha test programmatically

I'm familiar with Mocha's support of delaying the root suite to enable performing asynchronous operations before a test is executed, by use of running Mocha with the --delay flag, but this impacts all tests.
Is it possible to do something like this on a test by test basis, without using the --delay flag?
An example of a working asynchronous test is below, but unfortunately not of all of our tests are async and call run(). Please note that the below example also leverages dynamically generating tests to generate a test for each URL that is detected during the asynchronous operation before the suite executes.
driver = await new Builder().forBrowser('chrome').build();
await driver.get('http://example.org');
await driver.findElements(By.css('article header a')).then(async function (anchors) {
Promise.all(
anchors.map(async anchor => {
return new Promise(async function (resolve, reject) {
try {
const href = await anchor.getAttribute('href');
urls.push(href);
resolve();
} catch (err) {
console.log('Catch')
reject(err);
}
})
})
).then(function () {
driver.quit();
describe('my suite', function () {
urls.forEach(function (url) {
it(`Loads ${url}`, async function () {
await driver.get(url);
await driver.getTitle().then(function (title) {
assert.strictEqual(1, 1);
});
});
});
});
run();
})
});
Following #destroyer's suggestion, I was successful in accomplishing something similar using asynchronous hooks (below), but cannot dynamically generate a separate test for each URL since Mocha does not delay the root suite execution.
describe('Async test suite', function () {
const getAnchors = function () {
return new Promise(async (resolve) => {
driver = await new Builder().forBrowser('chrome').build();
await driver.get('http://example.org');
await driver.findElements(By.css('article header a'))
.then(async (anchors) => {
resolve(anchors);
})
});
}
const getUrls = function (anchors) {
return new Promise(async resolve => {
for (i = 0; i < anchors.length; i++) {
urls.push(await anchors[i].getAttribute('href'));
if (i === (anchors.length - 1)) {
resolve(urls);
}
}
});
}
const iterateUrls = function (urls) {
return new Promise(async (resolve, reject) => {
for (i = 0; i < urls.length; i++) {
await driver.get(urls[i])
const thisUrl = await driver.getCurrentUrl();
try {
await assert.strictEqual(thisUrl, urls[i]);
} catch (err) {
reject(err);
break;
}
if (i === (urls.length - 1)) {
resolve();
}
}
})
}
async function asyncController() {
Promise.all([
anchors = await getAnchors(),
await getUrls(anchors)
])
}
// Trigger async functions here
before(function (done) {
asyncController()
.then(() => {
done();
})
});
// Close the browser after test completes
after(async function () {
await driver.quit()
});
describe('Checks URLs', function () {
it('Iterates over URLs', async function (done) {
try {
await iterateUrls(urls);
} catch (err) {
done(err);
}
});
});
});

Categories

Resources