Can I somehow get the fetch response in the first `then`? - javascript

I am trying to use the Fetch API. It seems from examples that a GET request needs one then to parse the response somehow.
Currently I am doing this
fetch(url)
.then(response => response.json())
.then(response => {
console.log(response);
});
However that first then seems like a boilerplate. I tried to avoid it, for example:
fetch(url)
.then(response => {
console.log(response.json());
});
But this logs me a pending Promise with status resolved.
I read other questions on this topic and read a bit about promises, but I couldn't understand if it's possible to combine it in a single then (if so - how?) or not.
For example, two of the answers here point out that
There is no need to use more than one '.then'
and
there is no good reason to have two .then() handlers as the code from each could have been combined into a single .then() handler
But I couldn't get that example to actually work - I still got a promise :)
On the contrary, the accepted anwser here explains that .then actually does something to the result (extracts the returned from promise), but I couldn't unserstand if I can somehow do that myself, say response.json().then() or response.json().getVal() or is the double-then syntax the only way.

It's quite simple: when you dispatch the a fetch() request, it returns a promise containing the response. That is resolved by the first .then(). Resolving this first promise actually returns Response.
Now this is the tricky part: the methods that read the body of the response, be it .json(), .text(), .blob().... all return promises. This means that you will need to resolve the second promise in order to get the parsed response.
The flow looks like this:
Make a fetch() request, and it returns a Promise of type Response
When you attempt to resolve the content of the Response, it will return a second Promise, whose type depends on the method you use (e.g. .json() returns an object, .text() returns string, .blob() returns Blob).
Resolve the second Promise, and you get your actual parsed response body
p/s: If you're not using fetch() in a top-level context (as of the time of writing top-level await is still not a thing), then you can use async/await to make your code a little more readable:
const response = await fetch(url);
const content = await response.json();
console.log(content);

The first promise returned by fetch can be useful in some cases. If you want to avoid boilerplate, you could simply create your own function:
function fetch_json(url, opts) {
return fetch(url, opts)
.then(resp => resp.json());
}
fetch_json(your_url)
.then(json => console.log(json));

These days I am using the async/await syntax which is the same thing, but looks less like a boilerplate to me.
const response = await fetch(url)
const data = await response.json()
console.log(data)

Related

Getting the result from a fetch function in javascript

I'm struggling to get the actual result data from an http fetch in js. I can do it using XMLHttpRequest, but I'd much rather use fetching. I've been trying to get the results from a fetch request like this: fetch('https://api.website.net/?);
I know that you need to assign a then function but nothing I have tried has worked I keep getting more promise objects and no results. I understand vaguely that you need to assign a .then function for once it resolves but I really cannot figure out how to do that.
(This is in browser js not node)
I think that you're getting confused with promises. The fetch function returns a promise and not the data.
I recommend that you read this modern tutorial: https://javascript.info/fetch
This is a very trivial function that gets the url and returns the response data in json:
async function callApi(url) {
const response = await fetch(url);
const data = await response.json();
return data;
}
let data = await callApi('https://api.website.net/?');
// do whatever you want with the data
When dealing with primises there are 2 syntaxes, the old .then() and the new async/await. Using async/await is better, in general, so I recommend using the async/await syntax. With .then() it would look like this:
// Not recommended alternative
fetch('https://api.website.net/?')
.then(response => response.json())
.then(data => {
// do whatever you want with the data
});
Try this:
fetch(....)
.then(response => response.json())
.then(data => /* use data */);

fetch repeatedly returns pending promise

I need to request data from my REST server to populate my UI (frontend). In doing so, I need to request some data from my and other servers. One such request is to get a list of states (provinces). I use fetch() and .json() to do this.
I see there are a few questions on SO addressing this issue mentioned below (here, here and a few more), however attempting their solutions/suggestions did not solve my problem.
e.g.
fetch("http://localhost:3443/app/location/provinces").then(e => e.json()).then(console.log);
Naturally fetch() being a network operation returns a promise, or I can use it with await & async. Also, .json() is also a promise. Since then() is called by resolve(data) to proceed to the next then() and .catch(error) processes reject(error), the request above should make sense.
Problem:
However, on each call to:
fetch("http://localhost:3443/app/location/provinces")
.then(e => e.json())
.then(console.log)
.catch(console.warn);
I get a PromiseĀ {<pending>}. Since it is the case, I tried the same with an async/await impl and receive the same "error":
(async () => {
let l = await fetch("http://localhost:3443/app/location/provinces");
console.log(l);
})();
What am I doing wrong/missing?

I have 2 snippets of code, one uses .then().catch and the other uses async/await. Are both snippets doing the exact same thing?

I'm trying to figure out whether I prefer .then().catch or asyc/await so I've been rewriting some of my code and seeing which way I like more. The issue is that I'm not exactly sure if the code behaves exactly the same way when using .then().catch and asyc/await. For example, here are 2 snippets of code:
axios.post('/createRoom', room)
.then((response) => {
this.createRoomWindow = false
this.servers[this.selectedServer].rooms.push(response.data.room)
})
.catch((error) => {
console.log(error);
});
and
try {
let response = await axios.post('/createRoom', room)
this.createRoomWindow = false
this.servers[this.selectedServer].rooms.push(response.data.room)
} catch (error) {
console.log(error)
}
I'm curious whether or not these 2 pieces of code do the exact same thing or are there any differences which I just can't see myself. For example, it is my understanding that in the first snippet, this.createRoomWindow = false will only happen once I've received the response even though it has nothing to do with the response and doesn't require it.
I'm not sure if the this.createRoomWindow = false will execute before or after the POST request is finished in the second snippet. I know this.servers[this.selectedServer].rooms.push(response.data.room) will definitely execute after the POST request since it uses part of the response. Considering this.createRoomWindow = false doesn't have to wait for the POST request to be done, I assume JavaScript would execute it before the POST is finished.
Am I correct? And IF I am correct, how can I make sure that a piece of code executes only after the POST request if finished when using async/await?
They are NOT the same. Await can be used together with a Promise (that is what's your first snippet is returning). From MDN:
The await operator is used to wait for a Promise. It can only be used inside an async function.
await
Most important:
If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise.
While Promises:
A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.
So take care because they are DEFINITELY NOT THE SAME. Please read provided docs very carefully.

Why does fetch require an extra 'then' when parsing json? [duplicate]

I've been messing around with the fetch() api recently, and noticed something which was a bit quirky.
let url = "http://jsonplaceholder.typicode.com/posts/6";
let iterator = fetch(url);
iterator
.then(response => {
return {
data: response.json(),
status: response.status
}
})
.then(post => document.write(post.data));
;
post.data returns a Promise object.
http://jsbin.com/wofulo/2/edit?js,output
However if it is written as:
let url = "http://jsonplaceholder.typicode.com/posts/6";
let iterator = fetch(url);
iterator
.then(response => response.json())
.then(post => document.write(post.title));
;
post here is a standard Object which you can access the title attribute.
http://jsbin.com/wofulo/edit?js,output
So my question is: why does response.json return a promise in an object literal, but return the value if just returned?
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.
This difference is due to the behavior of Promises more than fetch() specifically.
When a .then() callback returns an additional Promise, the next .then() callback in the chain is essentially bound to that Promise, receiving its resolve or reject fulfillment and value.
The 2nd snippet could also have been written as:
iterator.then(response =>
response.json().then(post => document.write(post.title))
);
In both this form and yours, the value of post is provided by the Promise returned from response.json().
When you return a plain Object, though, .then() considers that a successful result and resolves itself immediately, similar to:
iterator.then(response =>
Promise.resolve({
data: response.json(),
status: response.status
})
.then(post => document.write(post.data))
);
post in this case is simply the Object you created, which holds a Promise in its data property. The wait for that promise to be fulfilled is still incomplete.
In addition to the above answers here is how you might handle a 500 series response from your api where you receive an error message encoded in json:
function callApi(url) {
return fetch(url)
.then(response => {
if (response.ok) {
return response.json().then(response => ({ response }));
}
return response.json().then(error => ({ error }));
})
;
}
let url = 'http://jsonplaceholder.typicode.com/posts/6';
const { response, error } = callApi(url);
if (response) {
// handle json decoded response
} else {
// handle json decoded 500 series response
}
Also, what helped me understand this particular scenario that you described is the Promise API documentation, specifically where it explains how the promised returned by the then method will be resolved differently depending on what the handler fn returns:
if the handler function:
returns a value, the promise returned by then gets resolved with the returned value as its value;
throws an error, the promise returned by then gets rejected with the thrown error as its value;
returns an already resolved promise, the promise returned by then gets resolved with that promise's value as its value;
returns an already rejected promise, the promise returned by then gets rejected with that promise's value as its value.
returns another pending promise object, the resolution/rejection of the promise returned by then will be subsequent to the
resolution/rejection of the promise returned by the handler. Also, the
value of the promise returned by then will be the same as the value of
the promise returned by the handler.
The json() method is available on all fetch() function. The json() method returns a Promise. Remember, when returning a Promise, it is still pending because it is asynchronous (assuming the data is not there yet). So to get the data AFTER using the json() method, you need to use another then() method so it will just return the data after it arrives.
To answer your question, It is what it is, its just the way of doing that.
Its like Promise ---> Another Promise ----> data
Use await with responce.json() also
const responce = await fetch(url);
const result = await responce.json();
Use await with responce.json() also

What is the return type of the json method of a response?

I see some examples of Angular 2, which convert the response of http into JavaScript object like this:
http.get("http://....").subscribe(
response => {
let result = response.json();
},
error => {
console.log('cannot get api');
}
);
I also see some examples which state that json() method returns a Promise, which requires the function to be enclosed in then to obtain the result:
response.json().then(result => ({
///
});
Why does one use then and the other does not? What's the return of value of json and which should I use?
The examples where you see response.json() returning a Promise are part of the Fetch API, which is still experimental: https://developer.mozilla.org/en-US/docs/Web/API/Response
Body.json()
Takes a Response stream and reads it to completion. It
returns a promise that resolves with a JSON object.
The other example you see are from Angular's implementation which returns the data directly instead of a Promise: https://github.com/angular/angular/blob/master/packages/http/src/body.ts#L26

Categories

Resources