1
votes

I have a JS program that does a whole lot of fetch() calls to a specific API. I want to abstract all the fetch() calls into a single class called "apiService" so my code will be more readable. I want the apiService to apply some intelligence and then return responses to the caller, in the following ways: - apiService should check the response to see if there are errors present, which it must always process in the same way. - fetch() will sometimes receive a "res" that is raw data and should be used as is, and sometimes it'll received json that needs a .then(res => res.json().then(res applied so it can return an object.

So I can't just do a "return fetch(..." from apiService, because apiService needs to process one or more .then() blocks with the response. But I also need to return something that causes the calling code to work asynchronously and not block and wait.

Anyone know how I could structure the apiService function to process the html responses but also return asynchronously i.e. the calling function would receive the result object after the error checking etc.

4
res is not "raw data", it's a response object. And how do you want to check the response, how can you distinguish JSON from something else? - Bergi
Yes, thanks. As I understand it I can only know whether to anticipate json or blob or text etc from the API spec - or is there a more elegant way to test for response type and respond accordingly? - BruceM
Well, you could take a look at the Content-type header in the res object... Or look at the status code, if it's 200 it should always be the documented one. - Bergi
Content-type isn’t guaranteed to be present, is it? - BruceM
No, but I definitely wouldn't want to consume an API that doesn't provide one. It's one of the fundamental HTTP headers, and every proper server should set it. (Without a content type, all you can do is res.arrayBuffer() and have good judgement) - Bergi

4 Answers

2
votes

So I can't just do a "return fetch(..." from apiService, because apiService needs to process one or more .then() blocks with the response. But I also need to return something that causes the calling code to work asynchronously and not block and wait.

This gives me the feeling that you might be misunderstanding promises a little bit. Take this example:

const doAsyncWork = () => fetch('somewhere').then(() => console.log('fetch is complete'))
// use the above function
doAsyncWork().then(() => console.log('used the fetching function'))

The output of the above code will be

fetch is complete
used the fetching function

As you can see, by chaining then after the fetch call, you are actually returning the result of then, and not fetch. Another way to think of it is, what are you actually returning if you called

const result = a().b().c() // we are really returning the result of `c()` here.

With the above in mind you can most definitely do something like:

const apiCall = loc => fetch(loc).then(res => {
  // do things with your response

  return res
})

apiCall('someEndpoint').then(finalRes => {
  console.log('this is called after fetch completed and response processed')
})
0
votes

There is a nice article about it here called "Synchronous" fetch with async/await that will break it down for you in pieces.

In Short:

You can use await when using fetch():

const response = await fetch('https://api.com/values/1');
const json = await response.json();
console.log(json);

First we wait for the request to finish, Then we can wait for it to complete (or fail) and then pass the result to the json variable.

The complete example would be to use async, because `await won't work without it:

const request = async () => {
    const response = await fetch('https://api.com/values/1');
    const json = await response.json();
    console.log(json);
}

request();
0
votes

I thinks you can fulfill your requirement using Promise.all()

Here is an example for you.

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});

For more info you can refer:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

0
votes

You can use a library called axios instead of worrying about promises and data formats yourself.

However, if you still want to do it, use the following way.

you can use a method to create promises like this.

 makeRequest(url, requestData) {

        const response = await fetch(url, requestData)
            .then(response => { console.info('network request successful to', url); return response.json() })
            .then(json => {
                console.info('response received for request', url, requestData, json)
                return json;
            })
            .catch(e => {
                console.error('error at request', url, requestData, e);
                return e
            });
        return response;
    }

and use the promises like this

makeRequest('someurl', {
            method: 'GET'
        }).then(response=>{/*Your logic*/}).catch(error=>{/*Your logic*/});