3

I have a wrapper class for fetch promises and I wanted to console out http response statuses.

Currently the switch is inside the get() method. How can I shift this switch case into the error() method and use it as a "thenable"?

Take a look:

class CustomFetch {


  get(url) {

    return new Promise((resolve, reject) => {

      const getOptions = {
        method: 'GET',               // *GET, POST, PUT, DELETE, etc.
        mode: 'cors',                // no-cors, cors, *same-origin
        cache: 'no-cache',           // *default, no-cache, reload, force-cache, only-if-cached
        credentials: 'same-origin',  // include, *same-origin, omit
        headers: {
          // 'Content-Type': 'application/x-www-form-urlencoded',
          'Content-Type': 'application/json',
          // 'Content-Type': 'text/xml'
        },
        redirect: 'follow',          // manual, *follow, error
        referrer: 'no-referrer',     // no-referrer, *client
        // body:     JSON.stringify(params)   // body data type must match "Content-Type" header
      };

      // DO FETCH
      fetch(url, getOptions)

      .then(function(res) {

        // RESPONSE VALIDATION
        switch (res.status) {

          case 200:
            // code...
            console.info('HTTP GET response status:', res.status, 'OK');
            break;

          case 201:
            // code...
            console.info('HTTP GET response status:', res.status, 'Created');
            break;

          case 404:
            // code...
            throw new Error('HTTP GET response status: 404 Not Found.');
            break;

          case 500:
            // code...
            throw new Error('HTTP GET response status: 500 Internal Server Error.');
            break;

          case 503:
            // code...
            throw new Error('HTTP GET response status: 503 Service Unavailable.');
            break;
          
          default:
            // code...
            break;

          }

          return res;

        })

        .then(res => res.json())
        .then(data => resolve(data))
        .catch(err => reject(err));


    });

  }
  
  error(res) {
  
  // Here, for example..
  
  }
  
}

const http = new CustomFetch;

async function Run() {


// GET -> AWAIT...
const fetch1 = await http.get('https://jsonplaceholder.typicode.com/users/1')
.then(data => console.log(data))
.then(data => console.log('asycn/await: Resource Get Successful.'))
.then(data => console.log('_'))
.catch(err => console.log(err));

}

// RUN async /await fetch functions in procedural order.
Run();
suchislife
  • 4,251
  • 10
  • 47
  • 78
  • 2
    Please avoid the [explicit construction antipattern](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). – Patrick Roberts Aug 28 '19 at 22:13

2 Answers2

1

If I understand what you are asking properly, you want to move the switch statement out into the error method on the class?

Because it is in the promise chain the error method would need to return a promise.

Maybe something like the following would work:

error(res) {
  switch (res.status) {
    case 200:
      // code...
      console.info('HTTP GET response status:', res.status, 'OK');
      break;
    case 201:
      // code...
      console.info('HTTP GET response status:', res.status, 'Created');
      break;
    case 404:
      // code...
      throw new Error('HTTP GET response status: 404 Not Found.');
      break;
    case 500:
      // code...
      throw new Error('HTTP GET response status: 500 Internal Server Error.');
      break;
    case 503:
      // code...
      throw new Error('HTTP GET response status: 503 Service Unavailable.');
      break;
    default:
      // code...
      break;
    }
    return res.json();
}

You would also need to remove this statement:

.then(res => res.json())

that follows the call.

So your get method would now look like:

(EDIT: As it has been pointed out, we must try to avoid the explicit constructor antipattern, so we would instead return the entire promise and defer the resolution and rejection to the caller)

// code above the fetch...
return fetch(url, options)
  .then(this.error);
1

Here is the updated code to reflect the removal of explicit construction anti-pattern. This is thanks to Patrick's second comment.

This class contains GET, POST, PUT & DELETE.

The class uses fetch. Then you use async await functions to return them in order.

class CustomFetch {

  // METHOD: GET
  get(url) {

    const getOptions = {
      method: 'GET',               // *GET, POST, PUT, DELETE, etc.
      mode: 'cors',                // no-cors, cors, *same-origin
      cache: 'no-cache',           // *default, no-cache, reload, force-cache, only-if-cached
      credentials: 'same-origin',  // include, *same-origin, omit
      headers: {
        // 'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Type': 'application/json',
        // 'Content-Type': 'text/xml'
      },
      redirect: 'follow',          // manual, *follow, error
      referrer: 'no-referrer',     // no-referrer, *client
      // body:     JSON.stringify(params)   // body data type must match "Content-Type" header
    };

    // DO FETCH
    return fetch(url, getOptions)
      .then(this.getResStatus)

  }

  // ================================================================================

  // METHOD: POST
  post(url, params) {

    const postOptions = {
      method: 'POST',              // *GET, POST, PUT, DELETE, etc.
      mode: 'cors',                // no-cors, cors, *same-origin
      cache: 'no-cache',           // *default, no-cache, reload, force-cache, only-if-cached
      credentials: 'same-origin',  // include, *same-origin, omit
      headers: {
        // 'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Type': 'application/json',
        // 'Content-Type': 'text/xml'
      },
      redirect: 'follow',          // manual, *follow, error
      referrer: 'no-referrer',     // no-referrer, *client
      body: JSON.stringify(params) // body data type must match "Content-Type" header
    };

    // DO FETCH
    return fetch(url, postOptions)
      .then(this.getResStatus)

  }

  // ================================================================================

  // METHOD: PUT
  put(url, params) {

    const putOptions = {
      method: 'PUT', // *GET, POST, PUT, DELETE, etc.
      mode: 'cors', // no-cors, cors, *same-origin
      cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
      credentials: 'same-origin', // include, *same-origin, omit
      headers: {
        // 'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Type': 'application/json',
        // 'Content-Type': 'text/xml'
      },
      redirect: 'follow', // manual, *follow, error
      referrer: 'no-referrer', // no-referrer, *client
      body: JSON.stringify(params) // body data type must match "Content-Type" header
    };

    // DO FETCH
    return fetch(url, putOptions)
      .then(this.getResStatus)

  }

  // ================================================================================

  // METHOD: DELETE
  delete(url) {

    const deleteOptions = {
      method: 'DELETE', // *GET, POST, PUT, DELETE, etc.
      mode: 'cors', // no-cors, cors, *same-origin
      cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
      credentials: 'same-origin', // include, *same-origin, omit
      headers: {
        // 'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Type': 'application/json',
        // 'Content-Type': 'text/xml'
      },
      redirect: 'follow', // manual, *follow, error
      referrer: 'no-referrer', // no-referrer, *client
      // body:     JSON.stringify(params)   // body data type must match "Content-Type" header
    };

    // DO FETCH
    return fetch(url, deleteOptions)
      .then(this.getResStatus)

  }

  // ================================================================================

  // METHOD: GET RESPONSE
  getResStatus(res) {

    switch (res.status) {
      case 200:
        // code...
        console.info('HTTP response status:', res.status, 'OK');
        break;
      case 201:
        // code...
        console.info('HTTP response status:', res.status, 'Created');
        break;
      case 404:
        // code...
        throw new Error('HTTP response status: 404 Not Found.');
        break;
      case 500:
        // code...
        throw new Error('HTTP response status: 500 Internal Server Error.');
        break;
      case 503:
        // code...
        throw new Error('HTTP response status: 503 Service Unavailable.');
        break;
      default:
        // code...
        break;
      }
      // CONVERT TO JSON...
      return res.json();
  }

} // end Class {}

const http = new CustomFetch;

async function Run() {

// ================================================================================

// GET -> AWAIT...
const fetch1 = await http.get('https://jsonplaceholder.typicode.com/users/1')
.then(data => console.log(data))
.then(data => console.log('ASYNC/AWAIT: Resource Get Successful.'))
.then(data => console.log('|'))
.catch(err => console.log(err));

// ================================================================================

// POST data
const postData = {

 name:     'Mark Postman',
 username: 'markpostman',
 email:    'mpostman@email.com'
}

// POST -> AWAIT...
const fetch2 = await http.post('https://jsonplaceholder.typicode.com/users', postData)
.then(data => console.log(data))
.then(data => console.log('ASYNC/AWAIT: Resource Post Successful.'))
.then(data => console.log('|'))
.catch(err => console.log(err));

// ================================================================================

// PUT data
const putData = {

 name:     'Mark Putman',
 username: 'markpostman',
 email:    'mpostman@email.com'
}

// PUT -> AWAIT...
const fetch3 = await http.put('https://jsonplaceholder.typicode.com/users/1', putData)
.then(data => console.log(data))
.then(data => console.log('ASYNC/AWAIT: Resource Put Successful.'))
.then(data => console.log('|'))
.catch(err => console.log(err));

// ================================================================================

// DELETE -> AWAIT...
const fetch4 = await http.delete('https://jsonplaceholder.typicode.com/users/1')
.then(data => console.log(data))
.then(data => console.log('ASYNC/AWAIT: Resource Delete Successful.'))
.then(data => console.log('|'))
.catch(err => console.log(err));

}

// RUN async /await fetch functions in procedural order.
Run();
suchislife
  • 4,251
  • 10
  • 47
  • 78
  • `reject` isn't defined. You've gotten rid of the `Promise` constructor. You might as well forward the original rejection reason with a useful stack trace, rather than masking it with a `ReferenceError` caused by a programming mistake. If I'm not being clear enough, you do that by removing `.catch(err => reject(err));` and just ending it at `.then(this.getResStatus);` – Patrick Roberts Aug 29 '19 at 03:36
  • Ok. How would I catch the errors? Perhaps just remove reject and `.catch(err => console.log(err))`? – suchislife Aug 29 '19 at 03:43
  • When you write an API, it is the responsibility of the caller to handle the error, not the API. This is also true with synchronous code, not just with callbacks and promises. The reason is because the API should have no knowledge of how / if the caller should / will handle the error. If there is no way to gracefully handle it, then the caller will not `.catch()` it, which generates an "uncaught promise rejection". – Patrick Roberts Aug 29 '19 at 03:46
  • And no, `.catch(err => console.log(err))` is not a good way to handle it. First of all, the caller will no longer be able to inspect the rejection reason returned by the failed `fetch()`, and secondly the promise will always resolve, so the API creates added complexity for the caller by forcing it to check whether the resolved value is a response or `undefined`. – Patrick Roberts Aug 29 '19 at 03:49
  • I found this answer on here and I think this is what you mean by the correct way to use async await with catch. https://stackoverflow.com/a/32824050 – suchislife Aug 30 '19 at 14:37
  • 1
    He did correctly answer the question, and the point of that answer is to show how using `await` will cause a promise rejected by `response.text()` to be caught by `catch`, but in general you would not want to `return` from within a `catch` unless you want that value to be treated the same way as a successfully resolved value. In this particular case it's probably okay because an error message can, under certain circumstances, be interchangeable with a string response from a successful AJAX call. – Patrick Roberts Aug 30 '19 at 14:54