Why Does .Json() Return a Promise

Why does .json() return a promise?

Why does response.json return a promise?

Because you receive the response as soon as all headers have arrived. Calling .json() gets you another promise for the body of the http response that is yet to be loaded. See also Why is the response object from JavaScript fetch API a promise?.

Why do I get the value if I return the promise from the then handler?

Because that's how promises work. The ability to return promises from the callback and get them adopted is their most relevant feature, it makes them chainable without nesting.

You can use

fetch(url).then(response => 
response.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
console.log(res.status, res.data.title)
}));

or any other of the approaches to access previous promise results in a .then() chain to get the response status after having awaited the json body.

Why does Body.json() return a Promise?

Your second snippet doesn't work because response.json() aka body.json() doesn't resolve instantly.

This is because body.JSON() streams and returns a Response using a Promise asynchronously; which must then be captured by a then() callback in order to read / manipulate.

Such is the nature of Promises.

However, such a syntactic flow can still be achieved by leveraging async await.

fetch('https://freegeoip.net/json/8.8.8.8')
.then(async (response) => {
Object.keys(await response.json()).forEach((key) => {
console.log("got " + key)
})
})

JavaScript fetch API - Why does response.json() return a promise object (instead of JSON)?

because response.json() returns another promise (which is within your function body)

https://developer.mozilla.org/en-US/docs/Web/API/Body/json

Return json object from resolved promise and assign it after it is loaded

React components must render synchronously. This is how React was designed; it cannot and will never change. Any way you hear of to render asynchronously is an abstraction that caches old render results and returns them synchronously. For this reason, you'll never be able to do what your code indicates.

However, I suspect that you don't actually care if the function is called before the data loads (which is what your code suggests), but rather that you want it called and to display a loading menu until the asynchronous operation completes and gives you its result. This is a common problem in React and is solved by having an initial empty state that you later set.

import React, { useState } from 'react';
function MyComponent() {
// Since you're using TypeScript, you can type this by
// setting the generic parameter
// e.g. useState<Product[] | null>(null);
const [products, setProducts] = useState(null);
listProducts().then(function(loadedProducts) {
setProducts(loadedProducts);
});
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>text: {products !== null && JSON.stringify(products)}</Text>
</View>
);
}

To make this more ES6:

import React, { useState } from 'react';
const MyComponent = () => {
const [products, setProducts] = useState(null);
listProducts().then(setProducts);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>text: {products && JSON.stringify(products)}</Text>
</View>
);
}

There's just one problem with this: every time MyComponent is called, the data is loaded. You may want this in some circumstances, but for the vast majority of the time, you only want to reload the products when something changes. This is where the useEffect hook comes in. It accepts a function that will only be called if the dependency list has changed.

useEffect(() => {
// This is called once on the first render and only
// called again whenever either dep1 or dep2 changes

// Note that a change in dep1 or dep2 does not necessarily
// mean your component will be called again (i.e. be
// rerendered). That only happens if dep1 or dep2 came
// from a useState hook, because to change a value from
// a useState hook, you must call the setter, which tells
// React to rerender.
console.log(dep1 + dep2);
}, [dep1, dep2]);

If you add the current minute to the dependency list, the product list will update at most every minute.

useEffect(() => {
listProducts().then(setProducts);
}, [
// Time in milliseconds rounded down to the minute
Math.floor(Date.now() / (60 * 1000))
]);

If you only want to call the function once, make the dependency list empty. If you want to call it after every render, don't specify a dependency list at all (i.e. useEffect(callback)). Read more here.

A few other things: your App class is pointless. You may be used to object-oriented programming from languages like Java and C#, but modern JavaScript avoids classes as much as is reasonable. Moreover, you don't need to extend React.Component because you aren't ever rendering the class with React. I'd recommend moving the functions out of the classes. In addition, you seem unsure as to how Promises work. They are called asynchronously, the callback is called way after the enclosing function finishes unless you use async/await. I'll refactor this for you, but you really shouldn't be taking on something this difficult without the basics. Try this Promise guide for a start, then learn how async/await make it easy to avoid infinite .thens.

const getProducts = async () => {
const data = await listProducts();
// typeof isn't a function
console.log(typeof data, data);
}

const listProducts = async () => {
// Create the initDB() function the way I did this
const db = await initDB();
const favdrinks = await new Promise((resolve, reject) => {
db.transaction(async tx => {
const [tx, results] = await tx.executeSql(
'SELECT p.favorites FROM drinks p',
[]
);
const favdrinks = [];
console.log("Query completed");
var len = results.rows.length;
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
console.log(`Drinks favorited: ${row.favorites}`)
const { favorites } = row;
favdrinks.push({
favorites
});
}
console.log(favdrinks);
resolve(favdrinks);
})
});
// Refactor this function as well
await closeDatabase(db);
return favdrinks;
}

Putting it all together:

import React, { useState, useEffect } from 'react';
const listProducts = async () => {
const db = await initDB();
return new Promise((resolve, reject) => {
db.transaction(async tx => {
const [tx, results] = await tx.executeSql(
'SELECT p.favorites FROM drinks p',
[]
);
const favdrinks = [];
var len = results.rows.length;
for (let i = 0; i < len; i++) {
let row = results.rows.item(i);
const { favorites } = row;
favdrinks.push({
favorites
});
}
resolve(favdrinks);
})
});
await closeDatabase(db);
return favdrinks;
}

const MyComponent = () => {
const [products, setProducts] = useState(null);
useEffect(() => {
listProducts().then(setProducts);
}, []); // empty dependency list means never update
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>text: {products && JSON.stringify(products)}</Text>
</View>
);
}

EDIT: Seeing your comment on another answer, you aren't able to remove the class logic. I think you may be misunderstanding the requirements of whatever framework you are using, but if you're seriously unable to avoid it, you should create the object of the class you're using inside the useEffect callback.

Why my json is returning attached with Promise in nodejs function?

The function with async keyword in front of function definition returns the Promise type value.

So to get the value, you need to use await keyword like

let Val = await fileName.load();

Uncaught in promise while waiting for json response

You might think about having one function to get, parse, and return the JSON from the API (no need to wrap your fetch in a promise as it already returns one), and use your polling function to check the returned data. If it's correct call one function, otherwise poll getData again. If there's an API error log that instead.

I've used async/await here but the principles are the same.

// Simulates an API
// If the random number is a modulo of 5 set data
// to an object with a key/value pair, otherwise
// it set it to an empty object. If the random number
// is a modulo of 9 send an error, otherwise send the data.
function mockFetch() {
return new Promise((res, rej) => {
const rnd = Math.floor(Math.random() * 20);
const data = rnd % 5 === 0 ? { name: 'Bob' } : {};
setTimeout(() => {
if (rnd % 9 === 0) rej('Connection error');
res(JSON.stringify(data));
}, 2000);
});
}

// `getData` simply gets a response from the API and parses it.
// I had to use JSON.parse here rather that await response.json()
// because I'm not using the actual fetch API.
async function getData() {
const response = await mockFetch();
return JSON.parse(response);
}

// The `poll` function does all the heavy-lifting
// first we initialise `count`
async function poll(count = 1) {

console.log(`Polling ${count}`);

// Try and get some data. If it's not an empty object
// log the name (or in your case call loadGraph),
// otherwise poll the API again after two seconds.
// We wrap everything in a `try/catch`.
try {

const data = await getData();

if (data && data.name) {
console.log(data.name); // loadGraph
} else {
setTimeout(poll, 2000, ++count);
}

// If the API sends an error log that instead
// and poll again after five seconds
} catch (err) {
console.log(`${err}. Polling again in 5 seconds.`);
setTimeout(poll, 5000, 1);
}

}

poll();

How to get the JSON from promise

You could await the response:

fetch(url, options).then(async (response) => {
const data = await response.json();
console.log(data)
})


Related Topics



Leave a reply



Submit