Reputation: 5631
I have to get an Excel file from Box.com via API and convert the Excel to JSON. Later on this JSON will be rendered with Express and Handlebars.
I created the following function to fetch the Excel/JSON:
let getExternalFile = async () => {
const stream = await client.files.getReadStream('123456789');
const external = await fs.createWriteStream('/external.xlsx');
stream.pipe(external);
let finished = await external.on('finish', async () => {
const excel = await excelToJson({
source: fs.readFileSync(external.path),
header: {
rows: 1
},
columnToKey: {
A: "edate",
B: "esource"
},
sheets: ['Sheet1']
});
console.log(excel);
return excel
})
}
My problem now is that I do not know how to resolve the pending Promise when I do this:
let testData = getExternalFile();
console.log(testData); // Promise { <pending> }
I would download multiple files with such a function and would like it then to store in in my variable to pass it later to my express route.
app.get('/', (req, res) => {
res.render('stories.ejs', {
msg: "Welcome to stories.ejs",
odata: odata,
edata: edata,
...
});
});
Do I have to wrap it in an anonymous async function to call it?
Upvotes: 0
Views: 264
Reputation: 113994
async
/await
is not the panacea for all things asynchronous. They were designed to work only with Promises. Specifically, async
/await
does not work with event emitters. The correct implementation of getExternalFile
should be:
let getExternalFile = async () => {
const stream = client.files.getReadStream('123456789'); // no await here
const external = fs.createWriteStream('/external.xlsx'); // no await here
stream.pipe(external);
let finished = new Promise ((resolve, reject) => {
external.on('finish', async () => { // cannot use await here
const excel = await excelToJson({
source: fs.readFileSync(external.path),
header: {
rows: 1
},
columnToKey: {
A: "edate",
B: "esource"
},
sheets: ['Sheet1']
});
console.log(excel);
resolve(excel);
})
});
return finished; // return the promise so you can await it later
}
All non-promise functions (I will not call them non-"async" because it will confuse people with the difference between async asynchronous functions and non-async asynchronous functions because people sometimes use "async" to mean asynchronous and sometimes to mean the "async" keyword which are promise generating functions)... I digress... all non-promise functions don't work with await
. This includes things like event emitters x.on('some_event' ... )
.
For such cases you will need to wrap them in new Promise()
to convert them to Promises.
Now that we've re-written the above function we can simply await the result:
app.get('/', async /* <--IMPORTANT */ (req, res) => {
let testData = await getExternalFile(); // use await here!!
// ...
});
If you have multiple awaits then the function may be slow:
app.get('/', async /* <--IMPORTANT */ (req, res) => {
let testData = await getExternalFile();
let testData2 = await getExternalFile2(); // SLOW!
// ...
});
If you can get away with it you can execute the async functions in parallel:
app.get('/', async /* <--IMPORTANT */ (req, res) => {
let testDataArray = await Promise.all([
getExternalFile(), getExternalFile2() // FAST!
]);
// ...
});
In real code you should catch errors on code you are awaiting to prevent your server from crashing:
app.get('/', async /* <--IMPORTANT */ (req, res) => {
try {
let testData = await getExternalFile(); // use await here!!
// ...
}
catch (err) {
// ...
}
});
Alternatively you can use a middleware such as express-async-handler to catch async errors.
Upvotes: 2