4

I'm a bit of newbie to Node so be gentle. I'm creating an app for my wedding which takes an uploaded guestlist (in Excel file format) and turns it into a JSON array which I can then use to build profiles about each guest (dietary requirements, rsvp answer, etc).

So far I've got a form on the homepage which allows the user to upload the .xlsx file and when the form is submitted the user is redirected back to the homepage again.

I've created the following route:

router.post('/', guestsController.upload, guestsController.getGuestlist, guestsController.displayGuestlist ); 

and here's my guestsController:

const multer = require('multer'); const convertExcel = require('excel-as-json').processFile; const storage = multer.diskStorage({ //multers disk storage settings destination: function (req, file, cb) { cb(null, './uploads/') }, filename: function (req, file, cb) { var datetimestamp = Date.now(); cb(null, file.fieldname + '-' + datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length -1]) } }); exports.upload = multer({storage: storage}).single('file'); exports.getGuestlist = async (req, res, next) => { try { await convertExcel(req.file.path, null, null, (err, guestData) => { req.guestlist = guestData.map((guestObj) => Object.values(guestObj)[0]); }); console.log(req.guestlist); next(); } catch (e){ res.json({error_code:1,err_desc:"Corrupted Excel file"}); next(); } }; exports.displayGuestlist = (req, res) => { console.log(req.guestlist); }; 

At the moment because of the synchronous nature of the functions, displayGuestlist is returning undefined to the console because covertExcel has not finished grabbing the data. You can see I have tried to use the new async await syntax to resolve this but it hasn't fixed it unfortunately.

I have also tried putting the log in displayGuestlist in a timeout function which has proven that this is purely a timing issue.

Any helpers would be much appreciated.

2 Answers 2

9

It looks like convertExcel is not a Promise-returning function, but rather uses an old-school callback. await does not work with those, so it's instead awaiting Promise.resolve(undefined) since the function returns undefined, not a Promise. Thankfully, in Node 8 and later, there's a promisify utility to convert callback-style functions to Promise-returning functions so that await can be used.

const { promisify } = require('util'); const convertExcel = promisify(require('excel-as-json').processFile); // ... const guestData = await convertExcel(req.file.path, null, null); req.guestlist = guestData.map((guestObj) => Object.values(guestObj)[0]); 
Sign up to request clarification or add additional context in comments.

3 Comments

Ahh awesome. Yeah that makes total sense actually. Thanks I'll pull down those packages and give it a try.
util is a package built-in to Node, so you don't have to install anything.
Thanks so much Jacob. Worked like a charm!
1

You can encapsulate your code in a promise and await this promise to resolve.

exports.getGuestlist = async (req, res, next) => { let promise = new Promise((resolve, reject) => { convertExcel(req.file.path, null, null, (err, guestData) => { if(err) reject(err); else resolve(guestData); }); }); try { let guestData = await promise; req.guestlist = guestData.map((guestObj) => Object.values(guestObj)[0]); console.log(req.guestlist); next(); } catch (e){ res.json({error_code:1,err_desc:"Corrupted Excel file"}); next(); } }; 

2 Comments

I would imagine that this would have worked as well to be fair. As Jacob explained the cause of the issue was me trying to await a function which wasn't promise-returning. So wrapping everything in a promise would work as well. Thanks Henrique
Solved the issue beatifully without any additional libraries!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.