multiple Promises in nodejs wait till they all finish - node.js

I have this snippet of code in my nodejs application.
The problem is that it doesn't wait before it renders the config page and passes the object thebalances.
app.get("/config", (req, res) => {
var thebalances = [];
client.rest.account.listAccounts().then(accounts => {
accounts.forEach(element => {
client.rest.product.getProductTicker(element.currency + '-USD').then(priceInfo => {
thebalances.push({
currency: element.currency
});
});
res.render("config", { balance: thebalances });
})
});
I have modified it to this now: but i think it errors out on some and like mentioned below it is all failing.
// GET /about
app.get("/config", (req, res) => {
let thebalances = [];
client.rest.account.listAccounts().then(async accounts => {
let promises = [];
accounts.forEach(element => {
promises.push(client.rest.product.getProductTicker(element.currency + '-USD'));
});
await Promise.all(promises).then(
results => {
results.forEach(priceInfo => {
thebalances.push({ currency: element.currency });
});
}
).catch(
error => {}
)
console.log(results);
res.render("config", { balances: thebalances });
})
});
I'm not sure how i can handle the multiple variable# of promises inside the foreach loop. I need to let that do its thing first before i can render the config page.
Any help would be greatly appreciated. I'm using coinbase api.

client.rest.account.listAccounts().then(async accounts => {
let promises = [];
accounts.forEach(element => {
promises.push(client.rest.product.getProductTicker(element.currency + '-USD'));
});
await Promise.all(promises).then(
results => {
results.forEach(priceInfo => {
thebalances.push({currency: element.currency});
});
}
).catch(
error => {
}
)
res.render("config", { balance: thebalances });
Please read up on this ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
There's some con with using Promise.all if one fail its fail.
edit: missing await

If it's really the case that one or more promise is failing, and it's really the case that this is okay from an app perspective, then...
// ...
let promises = accounts.map(element => {
return getProductTicker(element.currency);
});
// ...
function getProductTicker(currency) {
return client.rest.product.getProductTicker(currency + '-USD').catch(error => {
// debug here, if the error is a problem, otherwise
return null
})
}
Now promise all will contain proper resolutions of getProductTicker as well as nulls wherever that call failed. To clean it up...
await Promise.all(promises).then(results => {
let maybeWeShouldDebugHere = results.filter(r => !!!r)
})

Related

Store data in an array using for each in node js sequelize

I am trying to push the fetched data in an array using foreach but it only returns the first data in the loop. Here is my code.
exports.getAllTrial = async function (req, res, next) {
try {
new Promise( async (resolve, reject) => {
var reservations = [];
await Schedule.getSchedule()
.then(data => {
data.forEach(async (element) => {
await saveReserve.getAllTrial({where: {scheduleID: element.id, date: "8/18/2020"}})
.then(trial => {
trial.forEach(response => {
reservations.push(response.scheduleID)
})
})
console.log(reservations);
resolve(reservations);
})
});
})
.then(value=>{
res.status(200).json(value);
})
.catch(err => {
console.log(err);
});
} catch (e) {
return res.status(400).json({ status: 400, message: e.message });
}
}
My expected output should be: [ 9, 10, 10 ] But it only returns [9].
Async code in a foreach loop is a bad idea, as it won't be executed one after the other. I suggest reading a bit more async/await and the concept of promise, as you are mixing things here (such as mixing await and .then). Also worth looking into Promise.all which will resolve a list of promises and array.map.
While I have no idea of what some variables such as saveReserve are supposed to be or do, your code might be simplified into:
exports.getAllTrial = async (req, res, next) => {
try {
const data = await Schedule.getSchedule()
const reservations = await Promise.all(
data.map(element => {
return saveReserve.getAllTrial({ where: { scheduleID: element.id, date: '8/18/2020' } })
})
)
return res.status(200).json(reservations)
} catch (e) {
return res.status(400).json({ status: 400, message: e.message })
}
}

How to use result of called 'then' for next 'then'?

I want to return what I get after an async call.
So
app.get("/books", async (req, res) => {
let books = await getBooks()
.then(json => {
res.status(200).send({"books": json});
});
});
Should wait on rendering the result until the called getBooks is done.
export async function getBooks() {
console.log("Getting books from cloud");
Book.findAll({
// ...
}).then(books => {
console.log("Got books");
return JSON.stringify(books, null, 4);
});
}
But right now the response gets rendered without actually waiting for the result.
You don't need to use promises. You can just use await and then use the result of that.
app.get("/books", async (req, res) => {
const books = await getBooks();
res.status(200).send({ books });
});
I'd highly suggest taking it a step further and using try/catch to handle failure cases
app.get("/books", async (req, res) => {
try {
const books = await getBooks();
res.status(200).send({ books });
} catch (error) {
// massage this to send the correct status code and body
res.status(400).send( { error });
}
});
You can use await in second method too:
export async function getBooks() {
console.log("Getting books from cloud");
var books = await Book.findAll({
// ...
})
if(books){
console.log("Got books");
return JSON.stringify(books, null, 4);
}
You just need to return your promise, and since you're just returning a promise you don't need async;.
app.get("/books", async (req, res) => {
let json = await getBooks()
res.status(200).send({"books": json});
});
export function getBooks() {
console.log("Getting books from cloud");
return Book.findAll({
// ...
}).then(books => {
console.log("Got books");
return JSON.stringify(books, null, 4);
});
}
app.get("/books", async (req, res) => {
let books = await getBooks();
res.status(200).send({"books": books});
})

use answer .then () outside and use in another part of the code in node js

How can I use the answer of a promise outside of. Then what should I do?
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
console.log(resp)
})
.catch(e => console.log(e))
}
});
console.log (resp) inside the .then () knows it and prints correctly, but when I want to know resp out of the forEach to use below, it says undefined
Thanks.
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
logMyData(resp);
})
.catch(e => console.log(e))
}
});
logMyData=(x)=>{
console.log(x);
}
This is just as simple as adding a helper function which executes inside your .then
Guessing that you want to be able to access the value within the forloop. Since self.campoSelects is a promise we can use async await.
// Call campo selects
function getCampoSelects(_self, tablaCampo) {
return new Promise(async (resolve, reject) => {
let campoData;
try {
campoData = await _self.campoSelects(tablaCampo);
} catch (err) {
reject(err);
}
resolve(campoData);
});
}
function happyLittleFunc() {
const arreglo = [];
arreglo.forEach(async (item) => {
if (item.tipoCampo === 3) {
let campoSelect;
// Unsure if you are setting self somewhere but it can be passed in here.
try {
campoSelect = await getCampoSelects(self, item.tipoCampo);
} catch (err) {
console.log(err);
return;
}
console.log(campoSelect);
}
});
}
happyLittleFunc();

Display all collections in mongodb+express

This is my code for sending data to a database:
app.post('/thanks', function(req, res) {
if (atendees.checkin === req.body.dbstring) {
dbConn.then(client => {
delete req.body._id;
const db = client.db('mydata')
db.collection(atendees.checkin).insertOne(req.body);
})
(...)
This is how I display on the page after clicking on a href link:
app.get('/view-feedbacks', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.collection(atendees.checkin).find({}).toArray().then(function(feedbacks) {
res.status(200).json(feedbacks);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
});
That works fine. How can I do something similar to display all collections from the database instead of just the individual ones?
This is what I tried to do:
app.get('/view-history', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.listCollections().toArray().then(function(collInfos) {
res.status(200).json(collInfos);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
});
But it just gives me the name of each collection. I want to show all collections and all of their elements.
Edit: my question is different from this one: MongoDB Show all contents from all collections .I'm trying to do this on express.js, not on the terminal
Edit2: Using db.collection:
app.get('/view-history', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.collections().then(function(feedbacks) {
res.status(200).json(feedbacks);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
But this gives the error: TypeError: converting circular structure to JSON
With async/await, this could be done:
app.get('/view-history', async (req, res) => {
try {
const client = await dbConn;
const db = client.db('mydata');
let collections = await db.collections();
let documents = await Promise.all(collections.map(async (collection) => {
let documents = await collection.find({}).toArray();
return Promise.resolve([collection.collectionName, documents]); // Retain collectionName
}));
// Format into an object that looks like `collectionName: documents`
let formatted = documents.reduce((obj, collection) => {
obj[collection[0]] = collection[1];
return obj;
}, {});
res.json(formatted);
} catch (e) {
console.error(e);
res.sendStatus(500);
}
});
A Promise-only approach:
app.get('/view-history', (req, res) => {
dbConn.then((client) => {
const db = client.db('mydata');
return db.collections();
}).then((collections) => {
return Promise.all(collections.map((collection) => {
return new Promise((resolve, reject) => {
collection.find({}).toArray().then((documents) => {
resolve([collection.collectionName, documents]);
}).catch(reject);
});
}));
}).then((documents) => {
let formatted = documents.reduce((obj, collection) => {
obj[collection[0]] = collection[1];
return obj;
}, {});
res.json(formatted);
}).catch((e) => {
console.error(e);
res.sendStatus(500);
});
});
The main reason this code is unnecessarily verbose is because instead of just returning a big array filled with arrays of documents, you probably want an object that retains the name of the collection, like so:
{
collection1: [...documents...],
collection2: [...documents...],
...
}
Instead of:
[
[...documents...],
[...documents...],
...
]
If you do want just a big array of each collection without caring about the names of the collections, it becomes much simpler:
async/await version:
app.get('/view-history', async (req, res) => {
try {
const client = await dbConn;
const db = client.db('mydata');
let collections = await db.collections();
let documents = await Promise.all(collections.map((collection) => collection.find({}).toArray()));
res.json(documents);
} catch (e) {
console.error(e);
res.sendStatus(500);
}
});
Promise-only version:
app.get('/view-history', (req, res) => {
dbConn.then((client) => {
const db = client.db('mydata');
return db.collections();
}).then((collections) => {
return Promise.all(collections.map((collection) => collection.find({}).toArray()));
}).then((documents) => {
res.json(documents);
}).catch((e) => {
console.error(e);
res.sendStatus(500);
});
});
Have you tried just db.collections()? If that also doesn't give what you need, you might have to invoke db.collection(<name>) on each of the names you get from listCollections.

Using async await properly in node js

To overcome callback hell in javascript, I'm trying to use async await from legacy code written in SQLServer procedure.
But I'm not sure my code might be write properly.
My first confusing point is when async function returns, should it return resolve() as boolean, or just return reject and handle with try-catch?
Here is my code snippets.
Please correct me to right direction.
apiRoutes.js
app.route('/api/dansok/cancelDansok')
.post(dansokCancelHandler.cancelDansok);
dansokCancelController.js
const sequelize = models.Sequelize;
const jwt = require('jsonwebtoken');
async function jwtAccessAuthCheck(accessToken) {
if (!accessToken) {
return Promise.reject('Empty access token');
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
return Promise.reject('TokenExpiredError.');
} else {
return Promise.resolve();
}
});
}
async function checkFeeHist(dansokSeqNo) {
let feeHist = await models.FeeHist.findOne({
where: { DansokSeqNo: dansokSeqNo}
});
return !!feeHist;
}
async function getNextDansokHistSerialNo(dansokSeqNo) {
....
}
async function getDansokFee(dansokSeqNo) {
....
}
async function doCancel(dansokSeqNo) {
try {
if (await !checkFeeHist(dansokSeqNo)) {
log.error("doCancel() invalid dansokSeqNo for cancel, ", dansokSeqNo);
return;
}
let nextDansokSerialNo = await getNextDansokHistSerialNo(dansokSeqNo);
await insertNewDansokHist(dansokSeqNo, nextDansokSerialNo);
await updateDansokHist(dansokSeqNo);
await updateVBankList(dansokSeqNo, danokFee.VBankSeqNo);
await getVBankList(dansokSeqNo);
} catch (e) {
log.error("doCancel() exception:", e);
}
}
exports.cancelDansok = function (req, res) {
res.setHeader("Content-Type", "application/json; charset=utf-8");
const dansokSeqNo = req.body.DANSOKSEQNO;
const discKindCode = req.body.HISTKIND;
const worker = req.body.PROCWORKER;
const workerIp = req.body.CREATEIP;
const accessToken = req.headers.accesstoken;
//check input parameter
if (!dansokSeqNo || !discKindCode || !worker || !workerIp) {
let e = {status:400, message:'params are empty.'};
return res.status(e.status).json(e);
}
try {
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
doCancel(dansokSeqNo).then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
});
});
} catch(e) {
return res.status(e.status).json(e);
}
};
You'll need to rewrite jwtAccessAuthCheck(accessToken) so that it keeps track of the outcome of its nested tasks. In the code you've written:
// Code that needs fixes!
async function jwtAccessAuthCheck(accessToken) {
// This part is fine. We are in the main async flow.
if (!accessToken) {
return Promise.reject('Empty access token');
}
// This needs to be rewritten, as the async function itself doesn't know anything about
// the outcome of `jwt.verify`...
jwt.verify(accessToken,"dipa",function(err){
if(err) {
// This is wrapped in a `function(err)` callback, so the return value is irrelevant
// to the async function itself
return Promise.reject('TokenExpiredError.');
} else {
// Same problem here.
return Promise.resolve();
}
});
// Since the main async scope didn't handle anything related to `jwt.verify`, the content
// below will print even before `jwt.verify()` completes! And the async call will be
// considered complete right away.
console.log('Completed before jwt.verify() outcome');
}
A better rewrite would be:
// Fixed code. The outcome of `jwt.verify` is explicitly delegated back to a new Promise's
// `resolve` and `reject` handlers, Promise which we await for.
async function jwtAccessAuthCheck(accessToken) {
await new Promise((resolve, reject) => {
if (!accessToken) {
reject('Empty access token');
return;
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
reject('TokenExpiredError.');
} else {
resolve();
}
});
});
// We won't consider this async call done until the Promise above completes.
console.log('Completed');
}
An alternate signature that would also work in this specific use case:
// Also works this way without the `async` type:
function jwtAccessAuthCheck(accessToken) {
return new Promise((resolve, reject) => {
...
});
}
Regarding your cancelDansok(req, res) middleware, since jwtAccessAuthCheck is guaranteed to return a Promise (you made it an async function), you'll also need to handle its returned Promise directly. No try / catch can handle the outcome of this asynchronous task.
exports.cancelDansok = function (req, res) {
...
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
return doCancel(dansokSeqNo);
})
.then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
})
.catch(e => {
res.status(e.status).json(e);
});
};
I strongly suggest reading a few Promise-related articles to get the hang of it. They're very handy and powerful, but also bring a little pain when mixed with other JS patterns (async callbacks, try / catch...).
https://www.promisejs.org/
Node.js util.promisify

Resources