I have an API calling function that I would like to return the response.json() content as well as the response.status together in a single object.
Like so:
const getData = data => {
return fetch('/api_endpoint',{
method: 'GET',
headers: {
'Content-type': 'application/json'
}
})
.then(response => {
return {
body: response.json(),
status: response.status
}
})
}
The trouble is that response.json() is a promise, so I can't pull it's value until it's resolved.
I can hack around it by doing this:
const getData = data => {
let statusRes = undefined;
return fetch('/api_endpoint',{
method: 'GET',
headers: {
'Content-type': 'application/json'
}
})
.then(response => {
statusRes = response.status;
return response.json()
})
.then(data => {
return {
body: data,
status: statusRes
}
}
)
}
But it just feels WRONG. Anybody have a better idea?
es6 async/await might help it look more clean
Use
async/await
. That will make things much cleaner:You also might want to add a
try / catch
block and ares.ok
check to handle any request errors or non 20x responses.There is no need for the variable if it bothers you, you can return tuples (array in ES).
In this case variable is save enough since it's used only once and within the same promise stack.
Or do as Joseph suggested:
update
Here I would like to explain why using await can lead to functions that do too much. If your function looks ugly and solve it with await then likely your function was doing too much to begin with and you didn't solve the underlying problem.
Imagine your json data has dates but dates in json are strings, you'd like to make a request and return a body/status object but the body needs to have real dates.
An example of this can be demonstrated with the following:
You could say you need a function that goes:
Say url is type a and promise of response is type b and so on and so on. Then you need the following:
a -> b -> c -> d ; [b,d]-> e
Instead of writing one function that goes
a -> e
it's better to write 4 functions:a -> b
b -> c
c -> d
[b,d] -> e
You can pipe output from 1 into 2 and from 2 into 3 with promise chain
1.then(2).then(3)
The problem is that function 2 gets a response that you don't use until function 4.This is a common problem with composing functions to do something like
a -> e
becausec -> d
(setting actual dates) does not care about response but[b,d] -> e
does.A solution to this common problem can be threading results of functions (I'm not sure of the official name for this in functional programming, please let me know if you know). In a functional style program you have types (a,b,c,d,e) and functions that go from type a to b, or b to c ... For a to c we can compose a to b and b to c. But we also have a function that will go from tuple
[b,d]
toe
If you look at the 4th function
objectAndResponseToObjectAndStatusObject
it takes a tuple of response (output of 1st function) and object with dates (output of 3rd function) using a utility calledthread
created withcreateThread
.The async await syntax of getData would look like this:
You could ask yourself is
getData
not doing too much? No,getData
is not actually implementing anything, it's composing functions that have the implementation to convert url to response, response to data ... GetData is only composing functions with the implementations.Why not use closure
You could write the non async syntax of
getData
having the response value available in closure like so:This is perfectly fine as well, but when you want to define your functions as an array and pipe them to create new functions you can no longer hard code functions in getDate.
Pipe (still called compose here) will pipe output of one function as input to another. Let's try an example of pipe and how it can be used to define different functions that do similar tasks and how you can modify the root implementation without changing the functions depending on it.
Let's say you have a data table that has paging and filtering. When table is initially loaded (root definition of your behavior) you set parameter page value to 1 and an empty filter, when page changes you want to set only page part of parameters and when filter changes you want to set only filter part of parameters.
The functions needed would be:
Now you have the behavior of initial loading as an array of functions. Initial loading looks like:
Page and filter change will look like:
That demonstrates easily defining functions based on root behavior that are similar but differ slightly. InitialLoad sets both page and filter (with default values), pageChanged only sets page and leaves filter to whatever it was, filterChanges sets filter and leaves page to whatever it was.
How about adding functionality like not making the request but getting data from cache?
Here is an example of your
getData
usingpipe
andthread
with an array of functions (in the example they are hard coded but can be passed in or imported).The array of functions is easy enough for JavaScript but gets a bit more complicated for statically typed languages because all items in the array have to be
T->T
therefor you cannot make an array that has functions in there that are threaded or go from a to b to c.At some point I'll add an F# or ReasonML example here that does not have a function array but a template function that will map a wrapper around the functions.