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
Related
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
I have the following
const ingredientsPromises = ingredients.map(ingredient =>
// Is this .map calling the api?
axios.post('/api/ingredient', ingredient)
)
await Promise.all(ingredientsPromises)
.then(consoleThen)
.catch(consoleCatch)
I'm making multiple calls to an api, but what I'm not sure is that when the promise will run, will it run inside .map or only in Promise.all?
Promise will run individually. It is up to you when do you want to do something with it.
If you want to do something after each individual request is completed then you can just use
const ingredientsPromises = ingredients.map(ingredient =>
// Is this .map calling the api?
axios.post('/api/ingredient', ingredient)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
)
Otherwise you can use the promise returned by 'all' function like this. If you want to do something after all promises are resolved.
const ingredientsPromises = ingredients.map(ingredient =>
// Is this .map calling the api?
axios.post('/api/ingredient', ingredient)
)
await Promise.all(ingredientsPromises)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Also await won't do anything here so you can remove it.
const ingredientsPromises = ingredients.map(ingredient =>
// Is this .map calling the api?
axios.post('/api/ingredient', ingredient)
)
Yes, this is initiating your API request(s).
A promise will always return a promise.
Lets see what's happening here,
First of all, ingredientsPromises is initialized with array of promises (each promise representing your API request in pending state).
At this point of time, all of your requests had been initiated.
from the docs,
The Promise.all() method returns a single Promise that resolves when all of the promises passed as an iterable have resolved or when the iterable contains no promises. It rejects with the reason of the first promise that rejects.
So, with Promise.all(ingredientsPromises) you are resolving all these pending promises, which returns another promise (which means you have received the API response) either in fulfilled state (in case no error occurs), or in rejected state (in case one or more requests fail)
I use bluebird map function for this
await bluebird.map(ingredients, (i) => axios.post('/api/ingredient', i))
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
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
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