Reputation: 13994
I have an express server which needs to fetch some data from multiple external sources for each request. This logic is seperated into multiple routers (some are not managed by me).
These routers are completely independent, so there is no need for one to wait on the other.
As an example I have the following code:
const router1 = express.Router();
const router2 = express.Router();
const router3 = express.Router();
const finalRouter = express.Router();
router1.use((req, res, next) => setTimeout(next, 2000));
router2.use((req, res, next) => setTimeout(next, 2000));
router3.use((req, res, next) => setTimeout(next, 2000));
finalRouter.use((req, res, next) => console.log('All done!'));
When I would normally use all these routers in my application, it will execute sequentially and print All done!
in 6 seconds.
But to improve the speed of my page I want to execute them in parallel, so they are all finished in 2 seconds. How can I do this?
Upvotes: 0
Views: 1073
Reputation: 82136
Individual routes definitely aren't needed here, you could just have a single route and use Promise.all to wait on all the requests. If they don't naturally support Promises, you could just wrap them in one e.g.
router.use(async (req, res, next) => {
await Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => resolve(), 2000);
}),
...
]);
next();
});
router.use((req, res, next) => {
console.log('All done!');
});
As per the discussion in the comments, if you don't have the ability to merge the routes into one - that's fine, and doesn't really matter. Thinking more about this, all you would really need is a wrapper to capture the "real" middleware, add it to a queue, and then move onto to the next. For this to work, you would at least need access to the code that set's up the routes (which it appears to have) e.g.
queue.js
module.exports = route => {
return (req, res, next) => {
// ensure we've got a queue
if (!(req.queue instanceof Array)) {
req.queue = [];
}
// queue the blocking route
req.queue.push(new Promise((resolve, reject) => {
// run real route, signal success or failure when done
route(req, res, err => err ? reject(err) : resolve());
});
next(); // move on, don't wait
};
}
Then in your routes
const queue = require('./queue');
...
router.use(queue((req, res, next) => setTimeout(() => next(), 2000))));
router.use(queue((req, res, next) => setTimeout(() => next(), 2000))));
router.use(queue((req, res, next) => setTimeout(() => next(), 2000))));
router.use(async (req, res, next) => {
await Promise.all(req.queue);
console.log('All done!');
}
Upvotes: 2
Reputation: 138457
You could do fake requests to all subrouters ( by abusing the internal handle method as found out by this answer):
const mainroute = Express.Router();
mainroute.get("/",function(req,res,next){
Promise.all([
new Promise(resolve =>
router1.handle(req,res, resolve)),
new Promise(resolve =>
router2.handle(req,res,resolve))
]).then(_=>res.end("all done"));
});
that could also be achieved manually by passing a promise array with the request, then you can await them all at the end:
const router1 = express.Router();
const router2 = express.Router();
const timer = ms => new Promise( res => setTimeout(res,ms));
router1.use((req, res, next) =>{
req.promises = [timer(2000)];
});
router1.use( req => req.promises[0] = req.promises[0].then(_=>"first done"));
router2.use((req, res, next)=>req.promises.push(timer(2000)));
router1.use("/test",router2);
router1.use((req,res)=> Promise.all(req.promises).then(_=>res.end("all done")));
Upvotes: 0