How to resolve multiple then() promise into one - javascript

In this code I used multiple then() methods, I just want to convert it into only one then, How it is possible.
getGreeting = () => {
fetch(url)
.then((response) => response.json())
.then((result) => result.data)
.then((data) => printCards(data))
.catch((err) => {
console.log(err);
});
};

notice the async and await keywords
fetch(url)
.then(async (response) => {
const json = await response.json();
printCards(json.data);
})
.catch((err) => {
console.log(err);
});
};

Related

Calling React js Fetch Calls In a Sequence

Someone, please help me to call these fetch Api's one after the other as I am using the data which is stored in the backend for my next request. I want them to be called sequentially one complete then next request this way.
Promise.all([
fetch(`http://localhost:5000/zoomapi`,
requestOptions),
fetch('http://localhost:5000/getId')
.then((res) => res.json())
.then((result) => {
this.setState({ zoomid: result });
}),
fetch(`http://localhost:5000/users/zoom?name=${this.state.zoomid}`)
.then((res) => res.json())
.then((result) => {
this.setState({ zoomdata: result });
})
])
.catch((error) => {
this.setState({ error });
});
})
.setState is not a synchronous operation, so you cannot use const id = this.state.zoomid, you can use result instead. In addition, your promises should be chained, not nested. Example:
fetch('http://localhost:5000/getId')
.then((res) => res.json())
.then((result) => {
this.setState({ zoomid: result });
return fetch(`http://localhost:5000/users/zoom?name=${result}`);
})
.then((res) => res.json())
.then((result) => {
this.setState({ zoomdata: result });
})
.catch((error) => {
this.setState({ error });
});

What would this code look like if instead of "async/await" use "then and catch"?

There is such a method in a React application:
fetchData = async () => {
try {
const response = await fetch(`https://........`);
const data = (await response.json()).group;
this.setState({
data: data,
car: Object.keys(data)[0]
},this.filter);
} catch(err) {
console.log("404 Not Found");
}
};
How to write this part without async / await syntax, and using then and catch?
Just like this!
fetchData = () => {
fetch("YOUR_URL")
.then(response => response.json())
.then(json => json.group)
.then(data => {
this.setState({
data,
car: Object.keys(data)[0]
},this.filter);
})
.catch(err => {
console.log("404 Not Found");
});
}
It's simply-
fetchData = () => {
fetch(url)
.then(res => res.json())
.then(data => {
this.setState({data: data.group, car: Object.keys(data.group)[0]})
})
.catch(err => console.log(err));
};
If fetch returns a Promise you can:
fetchData = () => {
fetch(url)
.then(res => ... )
.catch(err => ...)
};
fetchData = () => {
return fetch(`https://........`)
.then(response => {
const data = response.json().group;
this.setState({
data: data,
car: Object.keys(data)[0]
},this.filter);
}).catch(err => {
console.log("404 Not Found");
});
};
response.json() won't return a Promise so you don't need await

How to chain multiple fetch() promises?

The following code fetches a json list and then does another fetch call for each list item to change their values. The problem is that it’s not done synchronously. “new” is printed to the console before “update”.
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
data.forEach(function(e, index,array) {
fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
})
});
console.log("new", data)
});
Update
Here's how I incorporated #Andy's solution:
function fetchFoodDetails(id, index) {
return fetch(API_URL_FOOD_DETAILS + id)
.then(response => response.json())
.then(data => {
return [index, data];
});
}
function fetchDiary() {
return fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
return data;
})
}
(async () => {
const data = await fetchDiary();
console.log("old", JSON.stringify(data));
const promises = data.map((food, index) => fetchFoodDetails(food.id, index));
await Promise.all(promises).then(responses => {
responses.map(response => {
data[response[0]] = {...data[response[0]], ...response[1]};
console.log("update");
})
});
console.log('new', JSON.stringify(data));
})();
It was more difficult so I went with #connoraworden's solution. But I think it can be simplified.
Thanks for all your answers.
The best way to go about this is to use Promise.all() and map().
What map will do in this context return all the promises from fetch.
Then what will happen is await will make your code execution synchronous as it'll wait for all of the promise to be resolved before continuing to execute.
The problem with using forEach here is that it doesn't wait for asynchronous request to be completed before it moves onto the next item.
The code that you should be using here is:
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(async data => {
await Promise.all(data.map((e, index, array) => {
return fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
})
}));
console.log("new", data)
});
You shouldn't be using forEach here. The best solution is to use Promise.all which waits for an array of promises (fetch is a promise) to all resolve, after which you can process the data.
Here I've created a dummy fetch function with some sample data to quickly show you how that works.
const dummyObj = {
main: [ { id: 1 }, { id: 2 }, { id: 5 } ],
other: {
1: 'data1',
2: 'data2',
3: 'data3',
4: 'data4',
5: 'data5',
6: 'data6',
7: 'data7',
}
}
// The summy function simply returns a subset of the sample
// data depending on the type and id params after 2 seconds
// to mimic an API call
function dummyFetch(type, id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(id ? dummyObj[type][id] : dummyObj[type]);
}, 2000);
});
}
// In the first fetch we display the data, just
// like you did in your example
dummyFetch('main')
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
// Instead of a forEach use Array.map to iterate over the
// data and create a new fetch for each
const promises = data.map(o => dummyFetch('other', o.id));
// You can then wait for all promises to be resolved
Promise.all(promises).then((data) => {
// Here you would iterate over the returned group data
// (as in your example)
// I'm just logging the new data as a string
console.log(JSON.stringify(data));
// And, finally, there's the new log at the end
console.log("new", data)
});
});
Here's the async/await version:
const dummyObj = {
main: [ { id: 1 }, { id: 2 }, { id: 5 } ],
other: {
1: 'data1',
2: 'data2',
3: 'data3',
4: 'data4',
5: 'data5',
6: 'data6',
7: 'data7',
}
}
function dummyFetch(type, id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(id ? dummyObj[type][id] : dummyObj[type]);
}, 2000);
});
}
(async () => {
const oldData = await dummyFetch('main');
console.log("old", oldData);
const promises = oldData.map(o => dummyFetch('other', o.id));
const newData = await Promise.all(promises);
console.log(JSON.stringify(newData));
console.log('new', newData);
})();
fetch is a Promise. This is asyncronous call, so the "new" console.log runs before finished all the promises. Use Promise.all() for that.
You can do this so:
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
return Promise.all(data.map(food =>
fetch(API_URL_FOOD_DETAILS + food.foodid)
.then(resp => resp.json())
.then(json => {
// do some work with json
return json
})
))
})
.then(data => console.log('new', data))
Storing multiple responses in a single array
The following code fetches multiple keywords in queries and stores all the response of all three responses to the all array
let queries = ["food", "movies", "news"]
let all = []
queries.forEach((keyword)=>{
let [subres] = await Promise.all([fetch(`https://reddit.com/r/${keyword}/hot.json?limit=100`).then((response) => response.json())]);
all.push(subres)
})
//now you can use the data globally or use the data to fetch more data
console.log(all)
How to chain multiple fetch() promises?
You do it like how you have been doing it, just append another .then()
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
data.forEach(function(e, index,array) {
fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
})
.then(()=>{
console.log("new", data)
})
});
});
If you want show only once "console.log("new", data)", you can check it with the index, like this:
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
data.forEach(function(e, index,array) {
fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
if ((data.length - 1) === index) { // CHECK INDEX HERE IF IS THE LAST
console.log("new", data)
}
})
});
});
You will need a recursive function to do this.
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
recursiveFetch(data)
});
function recursiveFetch(initialData){
e = initialData[initialData.length-1]; //taking the last item in array
fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
initialData.pop() // removing last item from array, which is already processed
if(initialData.length > 0)
recursiveFetch(initialData)
})
}
Note: This is an untested code.

AsyncStorage in a non async function

The following code:
_loginAsync = async () => {
fetch('localhost:3000/login')
.then((response) => response.json())
.then((responseJson) => {
await AsyncStorage.setItem('my-item', responseJson.item);
this.props.navigation.navigate('Home');
})
.catch((error) => {
console.error(error);
});
}
throws the error: Can not use keyword 'await' outside an async function.
Which is the proper way to valorize the my-item?
Your .then callback isn't marked as async, only the outer _loginAsync function is.
_loginAsync = async () => {
fetch('localhost:3000/login')
.then((response) => response.json())
.then(async (responseJson) => {
await AsyncStorage.setItem('my-item', responseJson.item);
this.props.navigation.navigate('Home');
})
.catch((error) => {
console.error(error);
});
}
That said, it seems weird to mix all of the .then and await forms here.
Using Async/Await Only
I think this is the most readable version. We just use async/await to await the fetch directly instead of working with its promise.
_loginAsync = async () => {
try {
const response = await fetch('localhost:3000/login');
await AsyncStorage.setItem('my-item', response.json().item);
this.props.navigation.navigate("Home")
} catch(error) {
console.error(error);
}
}
Using Promises Directly
You can (pretty much) always use an async function as a normal function that returns a promise as well. So instead of awaiting AsyncStorage.setItem we can just use its promise as part of our chain by returning it from then.
_loginAsync = () => {
fetch('localhost:3000/login')
.then((response) => response.json())
.then((responseJson) => AsyncStorage.setItem('my-item', responseJson.item))
.then(() => this.props.navigation.navigate('Home'))
.catch((error) => {
console.error(error);
});
}
If you have to make it work for your code, make the anonymous function for the block where await occurs to async.
_loginAsync = async () => {
fetch('localhost:3000/login')
.then((response) => response.json())
.then(async (responseJson) => {
await AsyncStorage.setItem('my-item', responseJson.item);
this.props.navigation.navigate('Home');
})
.catch((error) => {
console.error(error);
});
}
But I prefer this is a much better approach and looks more readable. Try this code instead.
_loginAsync = async () => {
try {
const response = await fetch('localhost:3000/login');
const responseJson = response.json()
await AsyncStorage.setItem('my-item', responseJson.item);
this.props.navigation.navigate('Home');
} catch (error) {
console.error(error);
}
}

How to use AsyncStorage.setItem in fetch callback?

I got syntax error in my code.
async getJSON() {
await fetch(url)
.then((response) => response.text())
.then((responseText) => {
await AsyncStorage.setItem(STORAGE_KEY, responseText);
})
.catch((error) => {
console.warn(error);
});
},
Error is Unexpected token on await AsyncStorage.setItem(STORAGE_KEY, responseText);
I guess it's complaining await
For every await you need an async.
async getJSON() {
await fetch(url)
.then((response) => response.text())
.then(async (responseText) => { // <-- add async here
await AsyncStorage.setItem(STORAGE_KEY, responseText);
})
.catch((error) => {
console.warn(error);
});
},
Also... the point of async/await is to make the code look sync when is actually async. You can rewrite the above code like this:
async getJSON() {
try {
var response = await fetch(url);
var responseText = response.text();
await AsyncStorage.setItem(STORAGE_KEY, responseText);
} catch(error) {
console.warn(error);
}
}

Categories