How to store value after running fetch() in javaScript? - javascript

I want to update state based on the response of fetch() function but i am unable to get the value instead getting Promise {}.If i console.log() the value is correct there.
state.product = fetch("https://fakestoreapi.com/products/2")
.then((res) => res.json()
.then((json) => {
console.log(json); // Correct value
return json;
})
);
console.log(state.product); // Promise {<pending>}

Fetch return a Promise that resolves to a Response object.
Your second console.log evaluate to a Promise because at this time the Promise has not resolved yet.
Maybe you can try the following :
async function getProduct(id) {
return fetch(`https://fakestoreapi.com/products/${id}`)
.then((res) => res.json())
.then((data) => data);
}
const product = await getProduct(2);
console.log(product);

Related

pending promise inside async/await

I wast trying to fetch some data in componentDidMount and then set it to state, wrote an async await function for the same. But if i was trying to set the state with the values right after await, the value was getting set to a pending promise but console logging would give the correct output.
For that reason i called the getData().then() to set the data, why it was giving pending promise can someone clear the concept out here?
componentDidMount() {
async function getData() {
const baseUrl = `https://........`;
const response = await fetch(baseUrl);
if (response.status === 200) {
const json = await response.json();
const { data } = json;
//console.log(data) =>correct output
return data;
}
return null;
}
getData().then(data => {
this.setState({ names: data });
});
}
You can simply do it like that:
componentDidMount() {
const baseUrl = `https://........`;
fetch(baseUrl)
.then((response) => response.json())
.then(result => {
this.setState({ names: result.data});
});
}
getData is an async function that you syncronize your fetch call inside. So it returns a promise. You're console logging inside that function after response fetched. So it logs the data.
You can think it as Fetch function that you use, you're awaiting it because it's a async function and you should await for the response. It's the same for the getData too. No matter how you wait, use then or await.

Can't call fetch api multiple times

I want to call this api multiple times in my project and when I am calling it , It continues giving an error which is
TypeError: Failed to execute 'json' on 'Response': body stream already
read at main.js:Line number
My Code is as Follows
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
a.then((fetchdata) => {
return fetchdata.json()
}).then((readingData) => {
console.log(readingData)
}).catch((err) => {
console.log(err)
})
You're not calling fetch multiple times. You're calling it once, and then trying to read the response body multiple times. That's why the error says you're trying to read the body when the stream is already closed — it was closed when you were done reading it the first time.
If you want to use the data twice, store it somewhere and use it twice.
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
// **************** Use it twice here
}).catch((error) => {
console.log(error)
})
If you want to fetch it again because it may have been updated, call fetch again:
let thisIsUrl = 'https://api.covid19api.com/summary';
fetch(thisIsUrl)
.then((data) => {
return data.json();
}).then((apidata) => {
console.log(apidata);
return apidata;
}).catch((error) => {
console.log(error);
});
// Presumably this is later (in time), not immediately after the above
fetch(thisIsUrl)
.then((fetchdata) => {
return fetchdata.json();
}).then((readingData) => {
console.log(readingData);
}).catch((err) => {
console.log(err);
});
Finally, this seems unlikely, but if you really want to fetch it once and use that one result in multiple places via the promise chain, keep the promise from then rather than the promise from fetch:
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
});
a.then((apidata) => {
// ***** Use it here
}).catch((error) => {
console.log(error)
})
a.then((readingData) => {
// ***** And then again here
}).catch((err) => {
console.log(err);
});
Side note: Your code is falling prey to a footgun in the fetch API; I've written about it in this blog post. fetch only rejects its promise on network errors, not HTTP errors. You have to check for those yourself in the first fulfillment handler, by checking for ok on the response object:
fetch("/your/resource")
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status); // Or better, use an Error subclass
}
return response.json();
})
// ...
fetch returns Promise, generally, promises have something like state inside themself;
pending: initial state, neither fulfilled nor rejected.
fulfilled: meaning that the operation was completed successfully.
rejected: meaning that the operation failed.
(source)
So when we call them and get the value from them with then, catch and etc. then they change the state after that call. So here, when you read the value with a.then(…, the promise changes its state to fulfilled and you are not able to call it again, you need a new and fresh Promise, actually a new instance of the fetch.
I want to recommend you to use Promise.all().
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
Promise.all([a,a,a]);
.then(results => {
// Results are here.
});

Why does setState in this promise take longer to update than the same code as an async function?

I have 2 versions of the same fetch functions here, the first one uses promises, the second one async-await. To my understanding there should be no difference between those two, so I'd appreciate some clarification to what's actually happening here.
The following version, which uses async-await, sets the state by the time console log runs:
async fetchPostList(query) {
const response = await fetch(`https://hacker-news.firebaseio.com/v0/${query}.json`)
const data = await response.json()
this.setState({ postList: data });
console.log(this.state.postList);
}
Console: Array(474) [ ... ]
The version that uses promises sets state after the console log:
fetchPostList(query) {
fetch(`https://hacker-news.firebaseio.com/v0/${query}.json`)
.then( response => response.json())
.then( data => this.setState({ postList: data }))
console.log(this.state.postList);
}
Console: Array []
The first function is an async function. In an async function the code pauses while the awaited promises resolve.
async fetchPostList(query) {
const response = await fetch(`https://hacker-news.firebaseio.com/v0/${query}.json`)
/* Pause ...*/
const data = await response.json()
/* Pause ... */
this.setState({ postList: data });
console.log(this.state.postList);
}
The second function is not async, which means it runs to completion. It just fires off the promises and callbacks and continues, so the console.log() is called immediately after creating the promises.
fetchPostList(query) {
fetch(`https://hacker-news.firebaseio.com/v0/${query}.json`)
.then( response => response.json())
.then( data => this.setState({ postList: data }))
/* does NOT pause */
console.log(this.state.postList);
}
If you want to log the updated state you must wait the Promise to resolve some data, only when you have data you can log the data itself:
fetchPostList(query) {
fetch(`https://hacker-news.firebaseio.com/v0/${query}.json`)
.then( response => response.json())
.then( data => {
this.setState({ postList: data })
// here you have data
console.log(this.state.postList);
})
// here you do not have data
}

fetch response.json() and response.status

Is this the only way to use the body.json() and also get the status code?
let status;
return fetch(url)
.then((response => {
status = response.status;
return response.json()
})
.then(response => {
return {
response: response,
status: status
}
});
This doesn't work as it returns a promise in the response field:
.then((response)=> {return {response: response.json(), status: response.status}})
Your status is not visible in the second then. You can just get the two properties in the single then.
json() returns a new Promise to you, so you need to create your object inside the then of the result of that function. If you return a Promise from a function, it will be fulfilled and will return the result of the fulfillment - in our case the object.
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(r => r.json().then(data => ({status: r.status, body: data})))
.then(obj => console.log(obj));
The .json method returns a promise, not the parsed value itself. If you want to access both the response and the parsed value in the same callback, you'll need to use nested functions like this:
fetch(url)
.then(response => {
response.json().then(parsedValue => {
// code that can access both here
})
});
Alternatively, you can use await inside an asynchronous function to eliminate the need for callbacks.
const response = await fetch(url);
const parsedValue = await response.json();
// code that can access both here
Of course, you'll want to check for errors, either with a .catch(...) call on a Promise or with a try...catch block in an async function. You could make a function that handles JSON and error cases, and then reuse it for all fetches. For example, something like this:
function handle(response) {
if (response.ok) {
return response.json().then(parsedValue => {
// the status was ok and the body could be parsed
return Promise.resolve({ response, parsedValue });
}).catch(err => {
// the status was ok but the body was empty or not JSON
return Promise.resolve({ response });
});
} else {
return response.json().catch(err => {
// the status was not ok and the body was unobtainable/empty/not JSON
throw new Error(response.statusText);
}).then(parsedValue => {
// the status was not ok and the body was JSON
throw new Error(parsedValue.error.message); // assuming an error message is returned by our REST API
});
}
}
I don't think it's the best design pattern, but hopefully this clarifies how the fetch API works.
PS: I avoided naming any variable or property json since that is the name of the text format. Once it's been parsed, it is no longer JSON.
Using two 'then's seem unnecessary to me.
async/await could get the job done pretty easily.
fetch('http://test.com/getData')
.then( async (response) => {
// get json response here
let data = await response.json();
if(data.status === 200){
// Process data here
}else{
// Rest of status codes (400,500,303), can be handled here appropriately
}
})
.catch((err) => {
console.log(err);
})
Did you try this?
return fetch(url)
.then((r)=> {return {response: r.json(), status: r.status}})
I think the cleanest way is to create a Promise.all() with the pieces you need.
.then(response => Promise.all([Promise.resolve(response.ok), response.text()]))
Which can be written shorter as
.then(response => Promise.all([response.ok, response.text()]))
The promise returns an array with all of the results
.then(data => ({ status: data[0], response: data[1] }))

Javascript Fetch API - How to save output to variable as an Object (not the Promise)

Please, how can I save output of fetch to a variable - to be able to work with it as with an object?
Here is the code:
var obj;
fetch("url", {
method: "POST",
body: JSON.stringify({
"filterParameters": {
"id": 12345678
}
}),
headers: {"content-type": "application/json"},
//credentials: 'include'
})
.then(res => res.json())
.then(console.log)
The final console.log will show an object. But when I tried to save it to variable .then(res => obj = res.json()) than the console.log(obj) will not hold the Object, but the Promise.
Any idea please, how to turn it into an Object saved in the variable?
.json() is an async method (it returns a Promise itself), so you have to assign the parsed value in the next .then()
var obj;
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
.then(data => {
obj = data;
})
.then(() => {
console.log(obj);
});
Modern async/await equivalent
You have to await the .json() method.
async function foo() {
let obj;
const res = await fetch('https://jsonplaceholder.typicode.com/posts/1')
obj = await res.json();
console.log(obj)
}
foo();
Instead of storing in a variable, create a function that will return data, and then store it in a variable. So It can accessible in your whole file.
async function fetchExam(id) {
try {
const response = await fetch(`/api/exams/${id}`, {
method: 'GET',
credentials: 'same-origin'
});
const exam = await response.json();
return exam;
} catch (error) {
console.error(error);
}
}
Then call that function to get data
async function renderExam(id) {
const exam = await fetchExam(id);
console.log(exam);
}
Update
With the current version of Node.js v14.3.0 support Top-Level async-await
import axios from 'axios';
const response = await axios('https://quote-garden.herokuapp.com/api/v3/quotes/random');
console.log(response.data);
Running this file using node --harmony-top-level-await top-level-async-await.js
Output
More details: https://medium.com/#pprathameshmore/top-level-await-support-in-node-js-v14-3-0-8af4f4a4d478
You can do like this. First fetch the data and create a function to do something with the data.
And then pass the result to that function and access it anywhere.
fetch('https://pokeapi.co/api/v2/pokemon/ditto')
.then(jsonData => jsonData.json())
.then(data => printIt(data))
let printIt = (data) => {
console.info(typeof data)
}
let data = [];
async function getRandomUser(){
// gets the response from the api and put it inside a constant
const response = await fetch('https://randomuser.me/api');
//the response have to be converted to json type file, so it can be used
const data = await response.json();
//the addData adds the object "data" to an array
addData(data)
}
function addData(object) {
// the push method add a new item to an array
// here it will be adding the object from the function getRandomUser each time it is called
data.push(object);
//the fetched data is available only on this scope
console.log("This is the value of date inside the function addData:")
console.log(data)
}
//Calls the function that fetches the data
getRandomUser()
console.log("This is the value of data outside the scope")
console.log(data)
A simple and handy solution is :
function myFunc(success) {
//do what you want HERE.
console.log(success)
}
fetch('https://reqres.in/api/users?page=2')
.then(data => data.json())
.then(success => myFunc(success));
Easiest approach is to use async/await method.
Simply copy & paste the following code in your chrome dev console to see the magic:
async function githubUsers() {
let response = await fetch('https://api.github.com/users')
let users = await response.json()
console.log(users)
}
githubUsers()

Categories