20
\$\begingroup\$

I've written an ES6 function using the new fetch() API and returning a new resolved promise with a data object or a rejected promise with an error subclass.

I'm pretty new to both ES6 and Promises, and it looks a little unwieldy, so I'm wondering if there's a better way. I'm also wondering about how this relates to the "explicit promise construction antipattern".

Any suggestions for how to improve or simplify?

function get(url) {
 console.log('Making fetch() request to: ' + url);
 let promise = new Promise((resolve, reject) => {
 fetch(url).then(response => {
 if (response.ok) {
 const contentType = response.headers.get('Content-Type') || '';
 if (contentType.includes('application/json')) {
 response.json().then(obj => {
 resolve(obj);
 }, error => {
 reject(new ResponseError('Invalid JSON: ' + error.message));
 });
 } else if (contentType.includes('text/html')) {
 response.text().then(html => {
 resolve({
 page_type: 'generic',
 html: html
 });
 }, error => {
 reject(new ResponseError('HTML error: ' + error.message));
 });
 } else {
 reject(new ResponseError('Invalid content type: ' + contentType));
 }
 } else {
 if (response.status == 404) {
 reject(new NotFoundError('Page not found: ' + url));
 } else {
 reject(new HttpError('HTTP error: ' + response.status));
 }
 }
 }, error => {
 reject(new NetworkError(error.message));
 });
 });
 return promise;
}

asked Mar 22, 2016 at 19:42
\$\endgroup\$

1 Answer 1

33
\$\begingroup\$

A few comments:

1) fetch already returns a promise, which means this:

new Promise((resolve, reject) => {
 return fetch(url).then(response => {
 if (response.ok) {
 resolve(response)
 } else {
 reject(new Error('error'))
 }
 }, error => {
 reject(new Error(error.message))
 })
})

Is pretty much the same as:

fetch(url).then(response => {
 if (response.ok) {
 return response
 }
 return Promise.reject(Error('error'))
}).catch(error => {
 return Promise.reject(Error(error.message))
})

2) Keeping this in mind you can simplify your code with early returns and fewer branches:

function get(url) {
 return fetch(url).then(response => {
 if (response.ok) {
 const contentType = response.headers.get('Content-Type') || '';
 if (contentType.includes('application/json')) {
 return response.json().catch(error => {
 return Promise.reject(new ResponseError('Invalid JSON: ' + error.message));
 });
 }
 if (contentType.includes('text/html')) {
 return response.text().then(html => {
 return {
 page_type: 'generic',
 html: html
 };
 }).catch(error => {
 return Promise.reject(new ResponseError('HTML error: ' + error.message));
 })
 }
 return Promise.reject(new ResponseError('Invalid content type: ' + contentType));
 }
 if (response.status == 404) {
 return Promise.reject(new NotFoundError('Page not found: ' + url));
 }
 return Promise.reject(new HttpError('HTTP error: ' + response.status));
 }).catch(error => {
 return Promise.reject(new NetworkError(error.message));
 });
}
answered Mar 22, 2016 at 21:56
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.