How to chain axios HTTP requests - javascript

I have 2 async await functions - one of them makes axios post request and another one - axios get request.
How could I chain them so that I would wait until the post operation is done and after it is completed, fetch new and updated objects?
const expensesListToDB = async () => {
await axios.post('http://localhost:4000/app/expenseslist',
{
expenseTitle: Expense,
expenseAmount: Amount
});
}
const expensesListFromDB = async () => {
await axios.get('http://localhost:4000/app/expenseslist')
.then(
response => setExpenseAndAmountList(response.data && response.data.length > 0 ? response.data : []));
}
expensesListToDB();
expensesListFromDB();
Thanks!

you can put expensesListFromDB() in the 'then' method of the Axios.
const expensesListToDB = async () => {
await axios.post('http://localhost:4000/app/expenseslist',
{
expenseTitle: Expense,
expenseAmount: Amount
}).then(
response => expensesListFromDB()); }
const expensesListFromDB = async () => {
await axios.get('http://localhost:4000/app/expenseslist')
.then(
response => setExpenseAndAmountList(response.data && response.data.length > 0 ? response.data : [])); }
expensesListToDB();

I suppose the API is in your control, the answers given by others are viable i would imagine a system where you return the new document in the response of the post request and just use it from their making it one single request
To have error handling and fault tolerance you could design your API response like this
{ "statusCode":"200",
"event":"doc_added_successfully",
"newDocs":{
}

You can return Promise from expensesListToDB and in the .then block you can run the function expensesListFromDB
Once an HTTP POST request is made, Axios returns a promise that is either fulfilled or rejected, depending on the response from the backend service.
const expensesListToDB = () => {
return axios.post("http://localhost:4000/app/expenseslist", {
expenseTitle: Expense,
expenseAmount: Amount,
});
};
const expensesListFromDB = async () => {
await axios
.get("http://localhost:4000/app/expenseslist")
.then((response) =>
setExpenseAndAmountList(
response.data && response.data.length > 0 ? response.data : []
)
);
};
expensesListToDB().then((data) => {
expensesListFromDB();
});

Related

Get http response status code after response.json()

I would like to get http status code after response.json to use it in my logic later, can I do something with it?
function apiRequest(path, options) {
fetch("api/" + path, options)
.then(response => response.json())
.then(data => {
let res = {
code: 200 //I want to put http status code here,
data: data
}
return res;
})
}
This is slightly tricky using then (as you are currently doing) because you want to get data directly from the response (which is a promise) and some more data from the parsed body (which is another promise).
So you can wrap the status and the data promise in a Promise.all and return that from the first then:
const apiRequest = () => {
const url = "//swapi.dev/api/planets/1/";
fetch(url)
.then((response) => Promise.all([response.status, response.json()]))
.then(([status, data]) => console.log({status, data}))
}
… but it would be easier to use async/await syntax and ditch the callbacks and you then only have to worry about a single function (and therefore scope) rather than multiple.
const apiRequest = async () => {
const url = "//swapi.dev/api/planets/1/";
const response = await fetch(url);
const data = await response.json();
const status = response.status;
console.log({status, data})
}
As an alternative you could consider async/await. That way you have access to response and data at the same time more easily.
async function apiRequest(path, options) {
const response = await fetch("api/" + path, options)
const data = await response.json()
let res = {
code: response.status,
data: data
}
// Do something with res
}
Try this
function apiRequest(path, options) {
fetch("api/" + path, options)
.then(response => Promise.all([Promise.resolve(response.status), response.json()]))
.then(([status, data]) => {
let res = {
code: status //I want to put http status code here,
data: data
}
return res;
})
}
you can git it in the first then before you return response.json something like this
function apiRequest(path, options) {
fetch("api/")
.then((response) => {
let status = response.status;
console.log("status", status);
return response.json();
})
.then((data) => {
console.log(data);
});
}
apiRequest();

Fetching API data inside express route returns undefined when using then or await

The problem
I'm creating an express server which fetches currency exchange rates from an API. In the route I fetch those exchange rates via a helper function and I then want to return them in the response body on the format {data: rates, status: 200}.
However, the only thing returned from the route is {status: 200} as the exchange rates are undefined (pending promise). The fetch goes through successfully, i.e is not caught in the catch clause nor are the default results returned. They key 'rates' also exists.
I don't understand why this is undefined nor how to fix it as I am awaiting the response from the service before returning from the route. I've also tried encapsulating the router response in a .then clause instead of using await but I encounter the same problem.
API service fetch
require("isomorphic-fetch");
const { DEFAULT_RATES } = require("../resources/other/default_exchange_rates");
// Documentation: https://currencyfreaks.com/documentation.html
let domain = "https://api.currencyfreaks.com/";
/*
* Retrieve exchange rates from USD to other currencies.
* Return object format: {EUR: "2.0293", XXX: "0.55736", ...}
*/
exports.getExchangeRates = async () => {
return await (
fetch(`${domain}latest?apikey=${process.env.EXCHANGE_RATE_API_KEY}`)
.then((res) => {
if (res.status > 200) {
return DEFAULT_RATES;
}
let api_response = res.json();
return api_response.rates;
})
// Catch parse error
.catch((err) => {
console.log(err);
})
);
}
Route
const service = require("../services/currency_api");
const express = require("express");
const router = express.Router();
router.post("/", async (req, res) => {
try {
const rates = await service.getExchangeRates();
return res.status(200).send({ data: rates, status: 200 });
} catch (err) {
console.log(err);
return res.stats(400).send({ error: "An error occurred", status: 400 });
}
});
module.exports = router;
/*
Postman test response:
{
"status": 200
}
*/
Change callback containing:
let api_response = res.json();
To:
.then((res) => {
if (res.status > 200) {
return DEFAULT_RATES;
}
return res.json();
})
.then(api_response => api_response.rates);
The fetch response.json() method returns a promise, so to get the actual value you either have to await for it or return the promise and add another .then to wait for res.json() to resolve.
In turn, when you're not waiting for res.json() your promise resolves to undefined(api_response is a promise, and rates is undefined), and then data is also undefined
The working solution using baldrs answer
exports.getExchangeRates = () =>{
let url = `${domain}latest?apikey=${process.env.EXCHANGE_RATE_API_KEY}`;
return (
fetch(url)
.then((res) => {
if (res.status > 200) {
return DEFAULT_RATES;
}
return res.json();
})
.then((data) => data.rates)
.catch((err) => {
console.log(err);
})
);
}

Promise.all and building axios requests inside async function

I'm looping data to build multiple axios requests and in this async loop is an await for some data.
When I do this, Promise.all resolves before an error is thrown on one of the requests .catch(errror => {}) I purposely gave one bad data so the request would go to catch.
return new Promise((resolve) => {
let requestArr = [];
requestsData.forEach(async (data) => {
let waitForThisData = await someFunction(data.id);
requestArr.push(
axios
.post()
.then((response) => {})
.catch((error) => {})
);
});
Promise.all(requestArr).then(() => {
resolve(true);
});
});
I'm testing this with 3 requests, 1 of which will go to catch. When I added breakpoints Promise.all already resolves before going to one of the requests catch.
If I remove the await code then I get my expected output, catch triggers first before the Promise.all resolves.
So why does Promise.all resolve before one of the request finishes(errors and goes to catch)?
This will make your Promise.all resolve AFTER the catch inside the loop, or, indeed, after all the requests (successful or unsuccessful):
const axios = require('axios');
const someFunction = () => {
return new Promise(resolve => {
setTimeout(() => resolve('222'), 100)
})
}
const requestsData = ['https://httpstat.us/200', 'https://httpstat.us/205', 'https://httpstat.us/306']
const requestArr = requestsData.map(async data => {
let waitForThisData = await someFunction(data);
return axios.post(data)
.then(response => {})
.catch(error => console.log(error.toString()))
});
Promise.all(requestArr).then(() => {
console.log('resolved promise.all')
})
https://httpstat.us/306 will produce an erroneous call. You can try placing it anywhere within the requestsData array.
From what I can see, you are pushing promises to the requestArr asynchronously, so if you call promise.all on the array before it's filled. It's going to resolve when the promises in it resolve. By removing the await you are quickly pushing all the requests to the requestArr.
So to answer your question, it resolves because all of the requests in requestArr have resolved before the requestArr.push() has been called on your error promise.
You can try to use your own async foreach function to do this a better way.
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
Now you can await the foreach before calling the Promise.all().
let requestArr = [];
await asyncForEach(requestsData, async (data) => {
let waitForThisData = await someFunction(data.id);
requestArr.push(
axios
.post()
.then((response) => { })
.catch((error) => { })
);
});
Promise.all(requestArr).then(() => {
resolve(true);
});

AbortController not terminating fetch request

I'm attempting to create a helper function to automatically timeout fetch requests after 2000 ms. The following code does not abort the fetch request, and instead prints the request as normal after 4000 ms. The code appears to be working in browser but not in node.
require('isomorphic-fetch');
const AbortController = require('abort-controller');
const fetchTimeout = async url => {
const controller = new AbortController();
setTimeout(() => {
controller.abort();
}, 2000);
return fetch(url, { signal: controller.signal })
.then(response => {
return response;
})
.catch(error => {
throw new Error(error.message);
});
};
const getStock = async () => {
return await fetchTimeout('https://httpbin.org/delay/4').then(data =>
data.json()
);
};
( async () =>
console.log(await getStock())
)();
I was able to fix this issue by using the node-fetch library instead of isomorphic-fetch with no other implementation issues. I've logged a ticket here, hope this can help someone else experiencing this frusturating issue.

Promise, Async Await

setDeviceTimeout = id => timeout => {
const {onSetDevices, devices} = this.props;
var newDeviceList = devices.map(device => {
if (device.id === id) {
var newDevice = {
//...device,
timeout: timeout
};
deviceTable.oncePostDevice(newDevice).then( data => {
return newDevice = data
});
}
return device;
});
onSetDevices(newDeviceList);
}
So the issue I am having here is that the onSetDevices(newDeviceList) get's called before the devices.map() is finished. This is because the devices.map() has the call to a server oncePostDevice(newDevice), then returns the data and stores it in the newDevice variable and puts that into the newDeviceList array.
Because this happens onSetDevices doesn't include the the newDevice in the newDeviceList array of objects and when I set my redux state using onSetDevices, nothing has changed.
I am wondering how I turn this into an async, await or use a promise alone to finish the task of making onSetDevices wait for the devices.map() to finish.
Also here is the code for oncePostDevice:
export const oncePostDevice = (device) => new Promise(function(resolve, reject) {
fetch('https://url/devices/'+device.id, {
method: 'PUT',
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(device)
})
.then(response => response.json())
.then(
data => {return resolve(data)},
error => {return reject(error)}
)
.catch(err => console.error(this.props.url, err.toString()));
});
As you can see I already have a promise in here working and returning the data afterwards.
I just need to know how to make my setDeviceTimeout inner mapping function finish before I hit onSetDevices.
Here's how you could do it (explanations inline in code):
// make the function async
setDeviceTimeout = id => async timeout => {
const {onSetDevices, devices} = this.props;
// make a list of promises, not of devices
// note: mapping a value via an async function will create promises
const newDeviceListPromises = devices.map(async device => {
if (device.id === id) {
const newDevice = {
...device,
timeout: timeout
};
return await deviceTable.oncePostDevice(newDevice);
}
return device;
});
// wait for all promises to finish and what they return will be the devices
const newDeviceList = await Promise.all(newDeviceListPromises);
onSetDevices(newDeviceList);
};

Categories