HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMajor

Using fetch() and a new Promise object to get API results

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
newfetchpromisegetusingandresultsobjectapi

Problem

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;
}

Solution

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));
  });
}

Code Snippets

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))
  })
})
fetch(url).then(response => {
  if (response.ok) {
    return response
  }
  return Promise.reject(Error('error'))
}).catch(error => {
  return Promise.reject(Error(error.message))
})
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));
  });
}

Context

StackExchange Code Review Q#123577, answer score: 33

Revisions (0)

No revisions yet.