How can i fetch urls sequentially? - javascript

I have array that contain urls. Firstly I need to fetch first and second urls simultaneously and when one of this will be resolved, i will fetch next url. Repeat while all urls wont be fetched. How can i do this ?

You don't show any code, so I'll have to provide a generic example. fetch() returns a promise. If you want to run a loop sequentially waiting for the promise of each fetch() operation, then the simplest way to do that is with async and await:
async function someFunc(array) {
for (let item of array) {
let result = await fetch(/* pass arguments here using item */);
// process result here
}
return someValue; // this becomes the resolved value of the promise
// the async function returns
}
// usage
// all async functions return a promise
// use .then() on the promise to get the resolved value
someFunc(someArray).then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});

Related

Why can't using "await" for each mapped element in an array of promises be used instead of "promise.all"?

I have an array of fetch API calls that have been resolved as shown below:
let results = await Promise.all(promises);
I'm trying to get the result of each resolved fetch API call in the array. I tried console logging the results expecting the resolved responses
const newStuff = await results.map(async (response) => {
try {
const recipeDetail = await response.json();
return recipeDetail;
} catch (err) {
console.log("error1: ", err);
}
})
console.log(newStuff);
But I get back an array of pending promises.
However this below seems to work.
const newStuff = await Promise.all(results.map(async response => {
let recipeDetail = await response.json();
return recipeDetail;
}));
console.log(newStuff);
Why does the second example work when the first one doesn't?
Thanks in advance.
Remember that an async function always returns a promise.
In your version that doesn't work, you're awaiting the result of map, but there's no reason to do that, because map returns an array, not a promise. The array contains the promises from the calls to the async callback you provided map, but they're still pending since nothing has waited for them to settle (map knows nothing about promises). Your code within the async callback waits for the promises from fetch to settle before settling the async callback's promise, but nothing waits for the promise from the async callbacks to setting.
That's the purpose of Promise.all: It waits for the promises to settle and fulfills its promise with an array of the fulfillment values (not an array of promises).

Fetch API always returning a promise

I have the following code where I am calling an api
const fetchToken = async () => {
const response = await axios.post("http://localhost:3000/api/getRedisData",
{
key: process.env.AUTH_TOKEN_NAME!,
});
console.log(response); // this returns what I expect it to return
return response;
};
Then I am calling the function like this.
fetchToken();
Now, this function works just fine. But when I try to save the data of this response like the following.
token = fetchToken().then((r) => r);
It returns a promise and it never resolves. What is happening and how do I wait until this promise resolves. I just need to wait and get the data. Thanks in advance.
I updated this question.
Let's suppose I have the following object.
const authMachine = createMachine<toggleAuth>({
id: "auth",
initial: "unauthenticated", // I want to chage this value based on token
context: {
//
},
states: {
//
},
});
Now I want to update the initial property based on if I am getting a token or not. Right now I am doing something like this.
initial: token ? "authenticated" : "unauthenticated"
So what's wrong here?
If you are in an async function, you can just await it to get the data.
const token = await fetchToken();
console.log(token);
// do other stuff with token
If you aren't, then you need to do everything you want done with the token in your then method. This is because you are calling an async method and without the ability to await it. The execution will just continue. The then function is the callback that happens when your async method completes successfully.
fetchToken().then(token => {
console.log(token)
// do other stuff with token
});
All async functions return a promise. So your fetchToken() function will always return a promise. The resolved value of that promise will be whatever value you return from the fetchToken() function body.
So, the caller of fetchToken() has to use await or .then() to get that resolved value. There is no free lunch with async/await. It's still asynchronous. await gives you synchronous-like behavior inside the function, but not outside the function.
To explain a little further. As fetchToken() executes, as soon as it hits the first await, it immediately suspends further execution of the function body and then immediately returns an unresolved promise back to the caller. The caller then continues to execute and the caller has to use .then() or await to know when the body of fetchToken() is actually done and what its final returned value is.
Then, sometime later, the promises you used await on inside of fetchToken() will resolve or reject and when the JS interpreter is back to the event loop, then it will continue executing the rest of the body of fetchToken() after the await. When it finally gets to the end of the function body or encounters a return statement, then it resolves the promise that was previously returned and the caller that is using either await or .then() will get notified that it is done and will be given the final return value as the resolved value of that promise. The caller can then process that final value and do its thing.
So, you probably want to be using something like this to get the final value or the error:
fetchToken().then(token => {
console.log(token);
// use the token value here
}).catch(err => {
console.log(err);
});
// you cannot use the token value here
If the call to fetchToken() itself is inside an async function, then it can use await also:
try {
let token = await fetchToken();
console.log(token);
// use the token value here
} catch(err) {
console.log(err);
}

Async function without return different behavior?

Here is an example:
let test = async () => {
let res = fetch('https://jsonplaceholder.typicode.com/todos/1')
}
console.log(test()) // Promise {<resolved>: undefined}
The output is Promise {<resolved>: undefined} because i didn't return anything, and it resolved with undefined. I understand this.
Now, here is the second example:
let test = async () => {
let res = await fetch('https://jsonplaceholder.typicode.com/todos/1')
}
console.log(test()) // Promise {<pending>}
I'm still not returning anything, and i get Promise {<pending>}. Why is it happening ?
The difference is that in your first example, the fetch call is not awaited, and it simply returns a promise object that is assigned to the res variable, and then, the function immediately returns. The promise that fetch returns is lost.
The second example when you use the await keyword, the async function is "paused" to wait until the fetch call resolves, and it assigns the resolved value (the fetch response) to the variable.
Even if you are not returning anything, whenever you await a promise inside an async function, it will wait until it resolves or rejects.
If the promise you are awaiting rejects, it will cause an exception within the function that will end up in a rejection of the implicitly returned promise.

when trying push a data to my array into a promise this don't save the data

i have this code:
let splatshArtData = [];
splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
splatshArtData.push(splatshArtUrl);
});
console.log(splatshArtData);
I want add the "splatshArtUrl" to my array, but this don't work, when i try print the data this don't print nothing, i don't know how to do that, any idea?
The problem that you're facing here is that getSplatchArt returns a promise, and promises take time to resolve. You can therefore never guarantee that splatshArtData.push(splatshArtUrl); will run before the console.log.
The solution is to move all logic which requires data returned from the promise to within the promise callback. This can of course include calls to other functions.
// function to process the splashArtData - will be called from the promise
// when the promise is resolved.
function processSplashArt(data) {
// this will now print as you are expecting
console.log(data);
}
let splatshArtData = [];
splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
splatshArtData.push(splatshArtUrl);
// pass the splashArtData to the callback function - it's now ready
processSplashArt(slashArtData);
});
JavaScript is synchronous, thus each line of code will be executed right after one another.
If we annotate your code with line numbers like below
1. let splatshArtData = [];
2. splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
3. splatshArtData.push(splatshArtUrl);
});
4. console.log(splatshArtData);
you are assuming that it will run in the order of 1, 2, 3, 4, while in reality it will run in the order of 1, 2, 4, 3. Why is that? Because JavaScript is synchronous, and the function at line 2 is asynchronous, meaning that you'll have to await that before you can continue. If you don't the splatshArtData variable will be an empty array since the data hasn't been fetched yet.
If you want to return the fetched data and use it in another function, you shouldn't mix it will callbacks as suggested in another answer, but instead chain the promise and use the resolved value from the function that fetches the data.
function getSplatshArt() {
let splatshArtData = [];
//Return a promise
return splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
console.log(splatshArtData); //this will log out the data
splatshArtData.push(splatshArtUrl);
return splatshArtData; //this will be the resolved value from the promise
});
}
//Call the function and access the resolved data in the .then() block
getSplatshArt()
.then(data => {
console.log(data); //this is the data returned from the getSplatshArt function
});
Looking at your code, I get the impression that you're looping over an array of ID's, and if you want to fetch multiple values at once, this wont work since you have to handle multiple promises. But that is another question and I think you should do some more research of your own before asking about that.
Try this:
let splatshArtData = [];
splatshArt.getSplatchArt(participants[i].championId).then((splatshArtUrl) => {
splatshArtData.push(splatshArtUrl);
console.log(splatshArtData);
});
The function inside the then runs right after the asynchronous function getSplatchArt resolves it's promise, so it was running the console.log before the item is added to the array.

Execute native js promise in series

I have to call a promise for some async task for each item in an array but I would like to execute these serially.
Promise.all is useful only to have a new promise that merges a list of promise but it does not invoke them in a sequence.
How can I achieve this using standard promise api without third-party libraries like Q, bluebird....
You chain promises using .then() with a callback that returns another promise. So, let's say you have three functions a, b and c that all return a promise. You can chain them (execute in sequence) like this:
a().then(b).then(c).then(function(result) {
// all are done here
});
If you are processing an array and you have a promise-returning function myFunc that you want to call for each item in the array, you can use a standard design pattern for arrays and promises with .reduce() to walk through the array one item at a time like this:
var items = [...];
items.reduce(function(p, item) {
return p.then(function() {
return myFunc(item);
});
}, Promise.resolve());
As it turns out this is really just chaining a bunch of .then() handlers like in the first example, but using the structure of .reduce() to walk the array for you.
Starting with ES2017, you can also use async/await to serially process an array like this:
async function processArray(arr) {
for (let item of arr) {
let result = await somePromiseReturningFunc(item);
// do something with result here before
// going on to next array item
}
return someFinalResult;
}
processArray(someArray).then(result => {
// done processing array here
}).catch(err => {
// handle error here
});

Categories