I have multiple API calls to be made which fetch via an API, write data to DB via API, send output to front end via another API.
I have written async function with await like below.
The first two should run one after another but the third one can run independently and doesn't need to wait for the first two fetch statements to plete.
let getToken= await fetch(url_for_getToken);
let getTokenData = await getToken.json();
let writeToDB = await fetch(url_for_writeToDB);
let writeToDBData = await writeToDB.json();
let frontEnd = await fetch(url_for_frontEnd);
let frontEndData = await frontEnd.json();
What is the best way to handle such multiple fetch statements ?
I have multiple API calls to be made which fetch via an API, write data to DB via API, send output to front end via another API.
I have written async function with await like below.
The first two should run one after another but the third one can run independently and doesn't need to wait for the first two fetch statements to plete.
let getToken= await fetch(url_for_getToken);
let getTokenData = await getToken.json();
let writeToDB = await fetch(url_for_writeToDB);
let writeToDBData = await writeToDB.json();
let frontEnd = await fetch(url_for_frontEnd);
let frontEndData = await frontEnd.json();
What is the best way to handle such multiple fetch statements ?
Share Improve this question edited Sep 24, 2022 at 4:02 starball 51k32 gold badges206 silver badges892 bronze badges asked Nov 12, 2019 at 9:14 Yasar AbdullahYasar Abdullah 2112 gold badges3 silver badges5 bronze badges 6- 7 You could have a look at Promise.all. – Yannick K Commented Nov 12, 2019 at 9:18
- 2 @YannickK Would Promise.all be necessary here? Couldn't he just use .then() instead? He's not waiting for the pletion of both, but rather the first then second, then third irrespective of those two. – Kobe Commented Nov 12, 2019 at 9:28
-
1
@Kobe I think the main issue in this case is that OP wants to separate the server and client calls since they're not dependent on each other - and it would be silly performance-wise if they waited on each other - but if any of them fail you want a rejection. You're definitely right that he could do without
Promise.all
, but in this case I'd imagine it would be cleaner (and easier to build on in the future) if he wrapped everything in onePromise.all
call, specifically for error handling. – Yannick K Commented Nov 12, 2019 at 9:57 -
@Kobe Because
Promise.all
is essential for proper error handling and waiting for the pletion of both the first-then-second and the third promises. – Bergi Commented Nov 12, 2019 at 10:44 - 1 The simplest answer solves the problem best, but unfortunately was undeservedly downvoted. It’s worth giving it a shot, @Yasar Abdulllah. – AndreasPizsa Commented Nov 12, 2019 at 11:56
4 Answers
Reset to default 3There are many ways but the most universal is to wrap each async code path in an async function. This gives you flexibility of mix & matching async return values as you please. In your example you can even inline code with async iife's:
await Promise.all([
(async() => {
let getToken = await fetch(url_for_getToken);
let getTokenData = await getToken.json();
let writeToDB = await fetch(url_for_writeToDB);
let writeToDBData = await writeToDB.json();
})(),
(async() => {
let frontEnd = await fetch(url_for_frontEnd);
let frontEndData = await frontEnd.json();
})()
]);
It's easier if you work with promise "creators" (= function that return promises) rather than raw promises. First, define:
const fetchJson = (url, opts) => () => fetch(url, opts).then(r => r.json())
which returns such a "creator". Now, here are two utilities for serial and parallel chaining, which accept both raw promises and "creators":
const call = f => typeof f === 'function' ? f() : f;
const parallel = (...fns) => Promise.all(fns.map(call));
async function series(...fns) {
let res = [];
for (let f of fns)
res.push(await call(f));
return res;
}
Then, the main code can be written like this:
let [[getTokenData, writeToDBData], frontEndData] = await parallel(
series(
fetchJson(url_for_getToken),
fetchJson(url_for_writeToDB),
),
fetchJson(url_for_frontEnd),
)
If you don't like the dedicated "creator" wrapper, you can define fetchJson
normally
const fetchJson = (url, opts) => fetch(url, opts).then(r => r.json())
and use inline continuations right where series
or parallel
are called:
let [[getTokenData, writeToDBData], frontEndData] = await parallel(
series(
() => fetchJson('getToken'),
() => fetchJson('writeToDB'),
),
() => fetchJson('frontEnd'), // continuation not necessary, but looks nicer
)
To bring the idea further, we can make series
and parallel
return "creators" as well rather than promises. This way, we can build arbitrary nested "circuits" of serial and parallel promises and get the results in order. Complete working example:
const call = f => typeof f === 'function' ? f() : f;
const parallel = (...fns) => () => Promise.all(fns.map(call));
const series = (...fns) => async () => {
let res = [];
for (let f of fns)
res.push(await call(f));
return res;
};
//
const request = (x, time) => () => new Promise(resolve => {
console.log('start', x);
setTimeout(() => {
console.log('end', x)
resolve(x)
}, time)
});
async function main() {
let chain = series(
parallel(
series(
request('A1', 500),
request('A2', 200),
),
series(
request('B1', 900),
request('B2', 400),
request('B3', 400),
),
),
parallel(
request('C1', 800),
series(
request('C2', 100),
request('C3', 100),
)
),
);
let results = await chain();
console.log(JSON.stringify(results))
}
main()
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use .then()
, rather than await:
fetch(url_for_getToken)
.then(getToken => getToken.json())
.then(async getTokenData => {
let writeToDB = await fetch(url_for_writeToDB);
let writeToDBData = await writeToDB.json();
// Carry on
})
fetch(url_for_frontEnd)
.then(frontEnd => frontEnd.json())
.then(frontEndData => {
// Carry on
})
Run independent request (writeToDB
) at the beginning and without await
let writeToDB = fetch(url_for_writeToDB);
let getToken = await fetch(url_for_getToken);
let getTokenData = await getToken.json();
// ...