I'm pretty noob in js. What I want to do is to send a get request to my server and receive a json. I can do this by fetch however, it returns a promise which is not suitable for me since I need to pass the received json to another function. Is there any way to get rid of promise? if not any alternative solution? I searched a lot but found no proper solution.
here's my function:
function fetchAndConvertData(path) {
return fetch(path).then(response =>
response.json().then(data => ({
data: data,
status: response.status
})
).then(async res => {
return res.data
}));
}
You can simply write :
async function fetchAndConvertData(path) {
const response = await fetch(path);
const data = await response.json();
return { // This is a Promise because async functions always return a Promise
data,
status: response.status
}
}
(async () => {
const result = await fetchAndConvertData("some/path/here");
console.log(result)
})();
Related
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.
I have this JavaScript fetch function
async getAllExpensesByUser() {
let reponse = await fetch("router.php/getAll");
console.log(reponse.json());
}
also tried
getAllExpensesByUser = async () => {
const response = await fetch("router.php/getAll");
if (response.ok) {
const jsonValue = await response.json();
return Promise.resolve(jsonValue);
} else {
return Promise.reject("*** PHP file not found");
}
};
my router.php file has this code
$data = $expensesController->getExpensesForUser($_SESSION['userid']);
echo json_encode($data);
but response.json() is returning PromiseĀ {<pending>} and the result I need is inside of [[PromiseResult]]
response.json() returns a Promise because this method takes a Response stream and reads it to completion, and this is an asynchronous process. You can find more information about it in the specification
What you need is just wait when the process of reading stream ends, for this purposes you can use await as well
async getAllExpensesByUser() {
let reponse = await fetch("router.php/getAll");
let object = await response.json();
return object
}
...
const userExpenses = await getAllExpensesByUser()
console.log(userExpenses)
...
As pointed out by Yousaf, both fetch and Body.json() return promises. To wait for both the fetch and Body.json calls to resolve before continuing, you simply need to update your code like this:
async getAllExpensesByUser() {
let reponse = await fetch("router.php/getAll");
let object = await response.json();
console.log(object);
return object;//If needed, see the comment by Emiel
}
I am struggling to extract data from an API call. I need it to return an array of keys but I keep getting [object Promise] when I try to render.
const apiKey = '*****';
const container = document.getElementById('root');
const Yelp = {
async search(term, location, searchBy) {
let response = await fetch(`https://cors-anywhere.herokuapp.com/https://api.yelp.com/v3/businesses/search?term=${term}&location=${location}&sort_by=${searchBy}`, {
headers: {
Authorization: `Bearer ${apiKey}`
}
})
if (response.ok) {
let jsonResponse = await response.json()
return jsonResponse.businesses.map(business => {
return business
})
}
}
}
console.log(Yelp.search('pasta', 'london', 'best_match'));
Since the Yelp.search function is async, it will always return a promise. That's the reason you observe a promise when you log the immediate result after calling it. Instead, you should await the promise like:
Yelp.search('pasta', 'london', 'best_match').then(results => {
console.log(results)
})
So to answer your question, you would call the then() method of the Promise to wait for its results to resolve. The then method takes two arguments. The first is a function that you provide to handle the results of the promise. The second is a function that you provide to handle any errors returned by the promise. For example:
Yelp.search('pasta', 'london', 'best_match').then(results => {
// handle the results
console.log(results)
}, err => {
// handle the error
console.error(err)
})
You may also catch exceptions thrown by the promise by invoking catch(), like so:
Yelp.search('pasta', 'london', 'best_match')
.then(results => console.log(results), err => console.error(err))
.catch(ex => console.error(ex))
I am trying to hook into the fetch request for some JSON and check if the data is in IndexedDB to get it before going to the network. Something odd is happening with my promises not resolving I think.
Here is my code:
event.respondWith(async function() {
var data = {success:true, msg:'', data: []};
localforage.iterate(function(value, key) {
data.data.push([key, value])
})
if (data.data.length > 0) {
return await new Response(JSON.stringify(data),{
headers: { "Content-Type" : "application/json" }
});
}
let response = await fetch(event.request)
// clone response as it can only be used once and needs to be returned for client fetch
let responseData = await response.clone().json()
await responseData.data.forEach(function (todo) {
localforage.setItem(todo[0], todo[1])
})
return response
}())
However, my front end gets an empty response object when the if resolves true (data in IndexedDB). It's as if my response object is being created before the IndexedDB promise resolves.
It's a bit of a guess without being able to run the code but
I think you need to wait for localforage.iterate since it returns a
promise
I think you also need to wait for localforage.setItem since
it returns a promise too - using Promise.all and map should handle that for you
var data = {success:true, msg:'', data: []};
await localforage.iterate((value, key) => {
data.data.push([key, value])
})
if (data.data.length > 0) {
return new Response(JSON.stringify(data),{
headers: { "Content-Type" : "application/json" }
});
}
let response = await fetch(event.request)
// clone response as it can only be used once and needs to be returned for client fetch
let responseData = await response.clone().json()
await Promise.all(responseData.data.map(todo => {
return localforage.setItem(todo[0], todo[1]);
}));
return response;
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] }))