In the following function, after creating the work document, I save the images from the request retrieved through multer module.When saving the image documents I try to update the work document by pushing all the _id
s of the images.
But somehow, if you take a look at the code bellow and focus on the console.logs, the second console.log is being executed first, although I used .then when creating the images.That also means that I get an outdated work document on the final lines of code.
The docs say that Model.create() returns a Promise, which means it should run synchronously if I use .then() (if I'm not mistaken). But this is not the case in my function:
function addToDB(req, res, model) {
let imagesToAdd = [];
Works.create(model)
.then(work => {
req.files.forEach(image => {
let path = image.path.split('').map(char => char === '\\' ? '/' : char).join('');
let imageObj = {
fileName: image.filename,
mimeType: image.mimetype,
imageURL: `${req.baseURL}/${path}`,
workId: work._id
};
imagesToAdd.push(imageObj);
});
Images.create(imagesToAdd)
.then(createdImages => {
let imageIds = [];
createdImages.forEach(image => {
console.log(image._id);
imageIds.push(image._id);
});
Works.updateMany({_id: work._id}, {$push: {images: {$each: imageIds}}}).catch(err => handleError(err));
})
.catch(err => handleError(err));
console.log('>'+work._id);
return work._id;
})
.then(workId => {
Works.findById(workId)
.then(foundWork => {
res.json(foundWork);
})
.catch(err => handleError(err));
})
.catch(err => handleError(err));
}
And here is the console after POSTing a work document:
cmd after execution:
And there is the response:
response after execution:
There 2 images were added.Above you saw in the response that images array doesn't have any element, while in mongo the image ids were saved:
The saved work after execution:
The end goal is to respond with the newly created work, which has the image ids included, so I can further populate the images array of the work document and workId in the image document.
How can I make the code run synchronously ?
The problem is that this:
Images.create(imagesToAdd)
.then(createdImages => {
let imageIds = [];
createdImages.forEach(image => {
console.log(image._id);
imageIds.push(image._id);
});
Works.updateMany({_id: work._id}, {$push: {images: {$each: imageIds}}}).catch(err => handleError(err));
})
is set to run asynchronously, then this:
console.log('>'+work._id);
return work._id;
is executed, you go to the next then
, and after some time the result of the first promise is returned.
The correct way would be:
function addToDB(req, res, model) {
let imagesToAdd = [];
Works.create(model)
.then(work => {
// Populate imagesToAdd
req.files.forEach(image => {
let path = image.path.split('').map(char => char === '\\' ? '/' : char).join('');
let imageObj = {
fileName: image.filename,
mimeType: image.mimetype,
imageURL: `${req.baseURL}/${path}`,
workId: work._id
};
imagesToAdd.push(imageObj);
});
Images.create(imagesToAdd)
.then(createdImages => {
// images have been created
let imageIds = [];
createdImages.forEach(image => {
console.log(image._id);
imageIds.push(image._id);
});
// imageIds has been populated
Works.updateMany({_id: work._id}, {$push: {images: {$each: imageIds}}})
.then(() => {
// After update is finished, findById
Works.findById( work._id )
.then( foundWork => {
// Return the work after it has been updated with images
res.json( foundWork );
} )
.catch( err => handleError( err ) );
})
.catch(err => handleError(err));
})
.catch(err => handleError(err));
})
.catch(err => handleError(err)); }
Even simpler way is to use async / await.