Reputation: 3807
I still cannot wrap my mind around async/await functionality and I have tried several solutions unsuccessfully for the following scenario:
I have this function:
function addDataToDb(articleData) {
initMongoDb();
kanpionDb.on('error', console.error.bind(console, 'connection error:'));
kanpionDb.once('open', function () {
SiteUrlModel.init().then(() => {
articleData.forEach(articleItem => {
var article = new SiteUrlModel(articleItem).save(function (error) {
if (error) {
console.log("Failed: " + articleItem.siteName);
} else {
console.log("Success " + articleItem.siteName);
}
});
});
});
});
}
which is called from within following another file:
c.on('drain', async function () {
Database.addDataToDb(ArticleList);
console.log("All done");
});
QUESTION: How can I make console.log("All done")
only display the message after Database.addDataToDb(ArticleList)
has finished executing?
Upvotes: 0
Views: 67
Reputation: 707158
This solution is kind of messy because you have a big mixture of asynchronous techniques all mixed in the same function: events, promises and callbacks and then on top of that, there's a loop. That is, indeed, a mess and is generally something you want to avoid.
The best overall solution to this level of complication is to back up a few steps and promisify all the basic asynchronous operations a few steps higher than this. For your database, you should go find and use the promise interface to the database. For your events, since these are both one-shot events you could build a promise wrapper on the object to get notified for the open and error events.
But, since I don't know all the components you're using I can't really do that part of it for you. So, we'll look at what we could do to patch in appropriate promise-level support into this mixture of techniques.
const {promisify} = require('util');
function addDataToDb(articleData) {
return new Promise((resolve, reject) => {
initMongoDb();
kanpionDb.on('error', err => {
console.error('connection error:', error);
reject(error);
});
kanpionDb.once('open', function () {
SiteUrlModel.init().then(() => {
return Promise.all(articleData.map(articleItem => {
let model = new SiteUrlModel(articleItem);
model.savePromise = promisify(model.save);
return model.savePromise().then(() => {
console.log("Success " + articleItem.siteName);
}).catch(err => {
console.log("Failed: " + articleItem.siteName);
throw err;
});
}));
}).then(resolve, reject);
});
});
}
// usage:
addDataToDb(articleData).then(() => {
console.log("All Done");
}).catch(err => {
console.log(err);
});
Summary of techniques:
.forEach()
loop gets replaced with Promise.all(articleData.map(...))
which will give us a promise that tells us when all the saves are done..save(callback)
has to be replaced with a promisified version of .save()
. Ideally, you'd just use a promisified version of your database interface here, but I've shown how to manually promisify the .save()
function if necessary.error
event.SiteUrlModel.init()
promise.Promise.all()
promise to the SiteUrlModel.init()
promise.Upvotes: 3
Reputation: 638
if you could please try this,
const saveData = (articleItem) => {
return new Promise((resolve, reject) => {
var article = new SiteUrlModel(articleItem).save(function (error) {
if (error) {
console.log("Failed: " + articleItem.siteName);
reject(error);
} else {
console.log("Success " + articleItem.siteName);
resolve('Success');
}
});
});
});
const addDataToDb = (articleData) => {
const list = [];
return new Promise((resolve, reject) => {
initMongoDb();
kanpionDb.on('error', console.error.bind(console, 'connection error:'));
kanpionDb.once('open', function () {
SiteUrlModel.init().then(() => {
articleData.forEach(articleItem => {
list.push(saveData(articleItem);
});
});
});
Promise.all(list).then(response => {
resolve(response);
}).catch(error => {
reject(error);
})
});
};
c.on('drain', async function () {
const result = Database.addDataToDb(ArticleList).then(res => {
console.log("All done");
});
});
I am trying to use the vanilla Promises
Upvotes: 0
Reputation: 93
You can use promises to make the caller wait for your async code to finish. Refactor your addDataToDb
function to return a promise and resolve/reject the promise accordingly. Since you've defined the "drain" callback to be an async function, you can use the await
keyword inside your callback to make things a bit more concise:
c.on('drain', async function () {
await Database.addDataToDb(ArticleList);
console.log("All done");
});
Upvotes: 0