How do I wrap this routine inside a Promise so that I only resolve when I get all the data?
var accounts = [];
getAccounts(userId, accs => {
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
});
})
});
EDIT: Any issues if I do it like this?
function getAccountsAllAtOnce() {
var accounts = [];
var required = 0;
var done = 0;
getAccounts(userId, accs => {
required = accs.length;
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
done = done + 1;
});
})
});
while(done < required) {
// wait
}
return accounts;
}
Let's put this routine into a separate function, so it is easier to re-use it later. This function should return a promise, which will be resolved with array of accounts (also I'll modify your code as small as possible):
function getAccountsWithTx(userId) {
return new Promise((resolve, reject) => {
var accounts = [];
getAccounts(userId, accs => {
accs.forEach(acc => {
getAccountTx(acc.id, tx => {
accounts.push({
'id': acc.id,
'tx': tx
});
// resolve after we fetched all accounts
if (accs.length === accounts.length) {
resolve(accounts);
}
});
});
});
});
}
The single difference is just returning a promise and resolving after all accounts were fetched. However, callbacks tend your codebase to have this "callback hell" style, when you have a lot of nested callbacks, and it makes it hard to reason about it. You can workaround it using good discipline, but you can simplify it greatly switching to returning promises from all async functions. For example your func will look like the following:
function getAccountsWithTx(userId) {
getAccounts(userId)
.then(accs => {
const transformTx = acc => getAccountTx(acc.id)
.then(tx => ({ tx, id: acc.id }));
return Promise.all(accs.map(transformTx));
});
}
Both of them are absolutely equivalent, and there are plently of libraries to "promisify" your current callback-style functions (for example, bluebird or even native Node util.promisify). Also, with new async/await syntax it becomes even easier, because it allows to think in sync flow:
async function getAccountsWithTx(userId) {
const accs = await getUserAccounts(userId);
const transformTx = async (acc) => {
const tx = getAccountTx(acc.id);
return { tx, id: acc.id };
};
return Promise.all(accs.map(transformTx));
}
As you can see, we eliminate any nesting! It makes reasoning about code much easier, because you can read code as it will be actually executed. However, all these three options are equivalent, so it is up to you, what makes the most sense in your project and environment.
I'd split every step into its own function, and return a promise or promise array from each one. For example, getAccounts becomes:
function getAccountsAndReturnPromise(userId) {
return new Promise((resolve, reject) => {
getAccounts(userId, accounts => {
return resolve(accounts);
});
});
};
And getAccountTx resolves to an array of { id, tx } objects:
function getAccountTransactionsAndReturnPromise(accountId) {
return new Promise((resolve, reject) => {
getAccountTx(account.id, (transactions) => {
var accountWithTransactions = {
id: account.id,
transactions
};
return resolve(accountWithTransactions);
});
});
};
Then you can use Promise.all()
and map()
to resolve the last step to an array of values in the format you desire:
function getDataForUser(userId) {
return getAccountsAndReturnPromise(userId)
.then(accounts=>{
var accountTransactionPromises = accounts.map(account =>
getAccountTransactionsAndReturnPromise(account.id)
);
return Promise.all(accountTransactionPromises);
})
.then(allAccountsWithTransactions => {
return allAccountsWithTransactions.map(account =>{
return {
id: account.id,
tx: tx
}
});
});
}
Firebase Cloud Functions: PubSub, "res.on is not a function"
TypeError: Cannot read properties of undefined (reading 'createMessageComponentCollector')
I am currently trying out this tutorial for node express with mongodb https://mediumcom/@sunnykay/docker-development-workflow-node-express-mongo-4bb3b1f7eb1e
I'm building a server for a simple web app that uses a MongoDB backend for storing dataI instantiate a new mongoose connection using:
How to deploy the localhost developed application with Angular+Node+MongoDB in productionPlease share the steps to follow
I'm new to MongoDB/Mongoose and trying to figure out how to map relationships between Schema