I'm trying to resolve this problem, I have a function that calls two other function that will push elements inside array. My problem is that in this way the first function function1 when it has finished calculating, it returns its value and the code continues. As soon as the second function finishes, it returns its value and the rest of the code is executed again.
What I would like to do is that the first function function1(..) is called as soon as it ends the second function function2(..) is called and when both arrays are full the code goes on. How can I do?
Thanks so much
async initialFunction(){
( async () => {
await function1(arrayPopulated1);
await function2(arrayPopulated2);
}) ();
// Code that use: newArray1 and newArray2 to write in the DB
}
async function1(array1){
for(let i = 0; i < array1.length; i++){
//operation there
// value = somedata
newArray1.push(value)
}
}
async function1(array2){
for(let i = 0; i < array2.length; i++){
//operation there
// value = somedata
newArray2.push(value)
}
}
EDIT:
The problem is that after function1 finishes its value returns to InitialFunction continue the code of this function and write in the db. It then returns the value of function2 to InitialFunction continues the code of this function and writes again to the DB.
I would like function1 to finish its for by writing to the newArray1 array, function2 to write the values to the newArray2 array and when both have been populated then InitialFunction writes to the db.
If you want both promises to complete before continuing, you can wrap both of the promises in a Promise.all.
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
Checkout MDN link
You don't have to wrap the promises again in another async function
async initialFunction(){
const newArray1 = [];
const newArray2 = [];
await function1(arrayPopulated1);
await function2(arrayPopulated2);
// Save to db
}
async function1(array1){
for(let i = 0; i < array1.length; i++){
newArray1.push(value)
}
}
async function1(array2){
for(let i = 0; i < array2.length; i++){
newArray2.push(value)
}
}
Related
So, i have a function in file that converts into bundler.js via webpack (devtool: 'eval-source-map'):
function getTable(tname, trowid) {
return get(ref(db, `table_name/${tname}/table_row_${String("0" + trowid).slice(-2)}`)).then((snapshot) => {
if (snapshot.exists()) {
console.log(snapshot.val()); // need to return this value
}
});
}
window.getTable = getTable;
and i need call this function in main.js smt like this:
for (i = 0; i < 69; i++) {
document.getElementById(i).value = window.getTable(tname, i);
}
I'd tried simply return this vale, do it global variable and more, but in result i'm still getting "undefined".
Instead of logging your value, return it to the Promise your getTable() function is returning with return snapshot.val() :
if (snapshot.exists()) {
return snapshot.val();
}
This makes it so getTable() returns a Promise that fulfills with the value that snapshot.val() returns. Now you have a few options. One is to use async/await on each promise returned by the getTable() function to "extract" the value from the Promise:
async function populateValues() {
for (let i = 0; i < 69; i++) {
document.getElementById(i).value = await getTable(tname, i);
}
}
populateValues();
This will call each getTable function one at a time, perform the asynchronous operation/code within it, wait for that to complete (ie: wait for the Promise getTable() returns to fulfill with a value), then update your element's .value. Once done, it will proceed to the next iteration, and it will do this 69 times. For something faster, you can kick off all your calls to getTable() in parallel, and then use Promise.all() to wait until all of them have been fulfilled:
async function populateValues() {
const promises = [];
for (let i = 0; i < 69; i++) {
promises.push(getTable(tname, i)); // kick off all your asynchronous calls (so that they run in parallel)
}
const tableValues = await Promise.all(promises); // wait for each async operation to finish, then iterate the data and update your UI
for(const [i, val] of tableValuese.entries()) { // you can use a standard `for` loop here to iterate your `tableValues` array if you'd like
document.getElementById(i).value = val;
}
}
populateValues();
Sorry about the confusing title.
So what I basically have is a function with a for-loop, calling another function in the loop, which has a call with an 'await' inside it. the function inside pushes values into an array once the async/await call is done. The value of the array is then returned once the loop is complete.
Something like this:
let globalArray = [];
const innerFunction = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 100);
});
};
const callingFunction = async () => {
let a = await innerFunction();
globalArray.push(a);
console.log(`GLOBAL ARRAY AFTER PUSH IN THE CALLING FUCNTION`);
console.log(globalArray);
};
const outerFunction = () => {
for (let i = 0; i < 1; i++) {
callingFunction();
console.log(`GLOBAL ARRAY AFTER FUCTION CALL`);
console.log(globalArray);
}
console.log(`GLOBAL ARRAY AFTER FOR LOOP END ${globalArray}`);
console.log(globalArray);
};
What I have observed is that the value of globalArray does not change in the logs both inside and right after the for loop (logs globalArray as []), but the log inside the callingFunction right after the push statement seems to log globalArray as [1].
This would indicate that the await statement isn't being waited upon by the primary function call in the for loop.
Ideally, I would expect all the log statements to log globalArray as [1].
Why would this be happening? Is there a better way to do this? I can't change the call pattern per se, because in my actual implementation, each function has additional things to do.
I've put this implementation up on stackblitz here: https://stackblitz.com/edit/typescript-i8jusx?file=index.ts
You are missing the await keyword in outerFunction when calling callingFunction.
const outerFunction = async () => {
for (let i = 0; i < 1; i++) {
await callingFunction();
console.log(`GLOBAL ARRAY AFTER FUCTION CALL`);
console.log(globalArray);
}
console.log(`GLOBAL ARRAY AFTER FOR LOOP END ${globalArray}`);
console.log(globalArray);
};
Remember that an async function automatically returns a Promise, even if it is Promise<void>
So I've read quite a few articles on the asyn nature of JS, so I'm kind of understanding what I'm doing wrong but I still don't understand how callbacks fix the asyn nature to allow you to retrieve a result. In my code, I am using the YouTube Data API to retrieve the video of movies that I am embedding in my project. I can get the video to console login to my function but I need that videoId outside of the function. How would I use a callback to extract that data?
Here is my code:
// Blank Variable to store videoId
var idForMovie = "";
async function getMovieTrailer() {
let resultAll = [];
for (var k = 0; k < 1; k++) {
let searchResults = await fetch("https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=" + encodeURIComponent(`${movieArray[k].Title} Trailer`)
+ "&key=" + apiKey);
let search = await searchResults.json();
resultAll.push(search);
}
for (var i = 0; i < resultAll.length; i++) {
var {items} = resultAll[i]; //object destructuring
console.log(items);
for (var j = 0; i < items.length; i++) {
var {videoId} = items[i].id; //object destructuring
console.log(videoId);
idForMovie = videoId;
}
}
//console.log(searchItems);
return idForMovie;
}
getMovieTrailer();
console.log(idForMovie);
Callbacks do not resolve that behaviour, if you do:
let variable;
object.onevent = (event) => variable = event
console.log(variable) // undefined
It's the same of doing:
let variable;
promise.then(result => variable = result)
console.log(variable) //undefined
This happens because the variable declaration and the console.log are placed in queue of the EventLoop synchronously hence executed sequentially, immediately, while both the callback mechanism and the promise mechanism are deferred.
If you want to do something relative to a global/upper-scoped variable, and you know that that variable will be changed as a consequence of an asynchronous task, you must necessarily do that inside the callback if you are using callbacks, or in the then() callback of a promise.
Consider two more things:
A callback based task, can easily be converted in Promise based:
object.onevent = (event) => // do something
becomes:
const promise = new Promise( resolve => object.onevent = resolve )
promise.then( event => // do something )
An async function as that one in your example, is just a function that returns a Promise, based on JavaScript generators, so inside of it it "looks" synchronous, but it all happens asynchronously:
const asynchronousFn = async () => {
const something = await somethingAsync()
doSomethingSynchronous()
return something
}
is the same of:
const asynchronousFn = () => somethingAsync().then(something => {
doSomethingSynchronous()
return something
})
The only real reason why Promises and then async/await were introduced in ECMA was to avoid Callbacks Hell phenomenum.
There are patterns that help you to manage this kind of scenarios, one of this is the Observable Pattern, and there are tools that help us doing that ( observables ) : https://rxjs.dev/guide/observable
But this is all another matter!
NOTE: There is an active stage4 proposal to introduce ES modules that are totally asynchronous. This means that you will be able to do something like:
let variable;
const somethingThatChangesVariableAsynchronously = async () => variable = "Something"
const somethingThatUsesVariable = () => console.log(variable)
await somethingThatChangesVariableAsynchronously()
somethingThatUsesVariable() // Something
This means that the whole module is asynchronous but you can write a sort of synchronous code inside of it as you do inside async functions.
https://github.com/tc39/proposal-top-level-await
You have to wait your getMovieTrailer() to return, then assign idForMovie to the returned value. For this you will need another async function, because await is only available within async functions
// Blank Variable to store videoId
var idForMovie = "";
async function getMovieTrailer() {
let resultAll = [];
for (var k = 0; k < 1; k++) {
let searchResults = await fetch("https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=" + encodeURIComponent(`${movieArray[k].Title} Trailer`)
+ "&key=" + apiKey);
let search = await searchResults.json();
resultAll.push(search);
}
for (var i = 0; i < resultAll.length; i++) {
var {items} = resultAll[i]; //object destructuring
console.log(items);
for (var j = 0; i < items.length; i++) {
var {videoId} = items[i].id; //object destructuring
console.log(videoId);
idForMovie = videoId;
}
}
//console.log(searchItems);
return idForMovie;
}
async function nextFunction() {
idForMovie = await getMovieTrailer();
console.log(idForMovie);
}
I have nested forEach loops out of which one of them contains another await call. I want my function doSomething to be executed only after the nested loop is done. As of now doSomething() gets executed first.
entity.forEach(function (dataItem) {
userParams.forEach(async function (userParamItem) {
const Marks = await _fetchMark(req, dataItem.MKTUNITID);
//do some manipulation to Marks
}
}
});
});
doSomething();
Whenever I have to use awaits inside a forEach (or any iterator loop; for, while, array-methods), I like to create a little array where I store the promises that are made.
I'll type out an example. I have a list of 10 urls I want to run a fetch towards, and store the results before running doSomething(). My runFetch()-function is asynchronous, and thus by default (unless I await it) returns a promise. After iterating, I will await ALL the promises at the same time, so they can run in parallell, but still wait for the slowest one.
const urls = [...] // my urls
const promises = []
for (const url of urls) {
promises.push( runFetch(url) )
}
const results = await Promise.all(promises)
//now that all my fetches are done, I can continue.
doSomething()
forEach can be bit tricky in such situations, you can simply use for loops instead as
they will not jump to next item until all promises are not resolved
for(i =0; i < entity.length; i++)
{
let dataItem = entity[i]
for(j =0; j < userParams.length; j++)
{
let userParamItem = userParams[i]
const Marks = await _fetchMark(req, dataItem.MKTUNITID);
//do some manipulation to Marks
}
}
Javascript does this because forEach is not promise-aware. It cannot support async and await so you cannot use await in forEach. Instead, use a for...of loop:
for(const dataItem of entity) {
for(const userParamItem of userParams) {
const Marks = await _fetchMark(req, dataItem.MKTUNITID);
//do some manipulation to Marks
}
}
});
doSomething();
This way the nested loops will execute first and doSomething() last.
Here is a nice post if you wanna learn more:
https://zellwk.com/blog/async-await-in-loops/
I have a case where I want to do something once 10 async calls have completed
let i = 0;
let array = [];
do {
this.service.getSomething(i).subscribe(response => {
array[i] = response;
});
} while (i < 10);
// how can I know when the 10 async calls have completed?
How can I achieve this?
This depends on whether you know the async operations (read Observables/Promises) beforehand or not.
For example if you can compose an array of Observables then the easiest way is to use forkJoin:
let observables = [ ... ];
Observable.forkJoin(observables)
.subscribe(results => /* whatever */);
Otherwise, you can just mergeMap them into a single chain a listen only to the complete signal:
Observable.range(1, 10) // or whatever
.mergeMap(i => /* return Observable here */)
.subscribe(undefined, undefined, () => console.log('all done'));
'The Rx way' is to use forkJoin:
const requestParams = [0,1,2,3,4,5,6,7,8,9];
const requests = requestParams.map(i => this.service.getSomething(i));
Observable.forkJoin(requests).subscribe(reponseArray => alldone(responseArray));
You have to make your loop asynchronous, so that an iteration will only occur when the next response is available. Here is how you can do that:
(function loop(arr) {
if (arr.length >= 10) return alldone(array); // all done
this.service.getSomething(arr.length).subsribe(response => {
loop(array.concat(response)); // "recursive" call
});
})([]); // <--- pass empty array as argument to the loop function
function alldone(arr) {
console.log(arr);
}
The loop function is immediately invoked with an empty array as argument. When you get the response, you call that function again, now with the extended array, ...etc. Once you have 10 responses, you call another function that will do something with the final array.
As you can see, I chose to eliminate the variable i, since arr.length has the same value.
Note that this kind of asynchronous processing can also be done with promises and some recent features like async and await. You might want to look into that. Here is an example
You can just count responses in separate variable, and check it before continue:
let i = 0;
let array = [];
var finishedCnt=0;
do {
this.service.getSomething(i).subsribe(response => {
array[i] = response;
finishedCnt++;
if(finishedCnt>=10) {
// all requests done, do something here
}
});
} while (i < 10);