Mongoose post 'init' is apparently unable to perform asynchronously as documented. I'm using the latest mongoose#5.0.10 and #types/mongoose#5.0.7 packages.
schema.post('init', function (doc, next: any) {
gunzipJSON(this.zipped).then(obj => {
this.categories = deserialize<Category[]>(Category, obj) || [];
next();
});
});
Also note my use of "next: any" to avoid a TS2349 error below on call to next(). When the hook fires, this and doc are set to a model. Before I upgraded to latest, I was using Mongoose 4.7.21 and next was supplied with a function(err) as expected, but the TS declaration must have been wrong.
Still, when the init hook function returns, the Mongoose init caller doesn't wait for my async decompression to complete and immediately returns the document to my client. When the decompression finishes, it correctly inflates and deserializes the field but it's too late. What am I doing wrong? I've tried numerous permutations of hook parameters to trigger async behavior without luck. Of course this happens when you're in a time crunch!
UPDATE:
I just now read 5.0 release notes https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md: "init hooks are now fully synchronous and do not receive next() as a parameter. Document.prototype.init() no longer takes a callback as a parameter. It was always synchronous, just had a callback for legacy reasons."
Yikes! How does one apply post load async hooks?
Related
I realize that the standard practice for promises in Mongoose is to use exec(), but the following works (or at least appears to) and I want to understand the flow. I'm not against using exec, I'm just exploring this a bit to learn.
In the following Mongoose operation:
let id:string;
SomeDocument.remove({}, (err) => { //clears collection
someDoc = new SomeDocument(data); //creates a new doc for the collection. Id is created here from what I understand.
someDoc.save( (err, result) => { doSomething(); });//doSomething gets called sometime in the future.
id = someDoc._id.toString();
}).then( (result) => {doSomethingElse(id)});//This works - but will doSomethingElse always be called after the first annonymous callback completes?
I get that doSomething() will just get called at some future point - no problem. The question is, will the first callback to the remove call complete prior to doSomethingElse in the then call being called. It seems to be, in that the id is correctly populated in doSomethingElse, but I want to make sure that isn't just a fluke of timing - i.e. I want to know if I can rely on that callback completing prior to the then. I'm using standard ES6 promises (NodeJS.Global.Promise).
The alternative is that maybe then is called after the remove operation completes, but prior to the callback completing (doesn't seem to - but I want to confirm).
Set me straight if I'm explaining this incorrectly.
Yes, as #JaromandaX explained in the comments the order of callbacks is deterministic.
However, it's still bad code, and you should not rely on this behaviour. If you are using promises, don't pass a callback to remove at all; only pass callbacks to then!
SomeDocument.remove({})
.then(() => {
const someDoc = new SomeDocument(data);
someDoc.save().then(doSomething); // doSomething will get called in the future.
return someDoc._id.toString();
//^^^^^^
})
.then(doSomethingElse); // doSomethingElse will get passed the id
doSomethingElse will get called with the result of the previous callback, which is guaranteed to have been completed for that.
I´m new to loopback and I´m trying to learn to use remote hooks now. I´ve read on the documentation I need to provide three parameters. The context, and unused variable and a next parameter.
I usually see that at the end of the remote hook next() is called.
Can someone explain to me what is the purpose of this parameter in loopback?
It's all about asynchronous nature of node.js.
The purpose of next is to tell Loopback that you've done all you needed to do in the hook and it can carry on processing.
Since you might need to do something asynchronous in the hook you need Loopback to wait for you to finish it, then you call next() and Loopback knows you're done.
And most importantly if you didn't call next() your app would hang which would result in 408 timeout.
For example if you needed to do request to another server:
SomeModel.beforeSave = function(next, modelInstance) {
// if you call next() here it would be called immediately and the result of request
// would not be asign to modelInstance.someProperty
request('http://api.server.com', function (error, response, body) {
// do something with result of the request and call next
modelInstance.someProperty = body;
// now when you updated modelInstance call next();
next();
})
// same here if you call next() here it would be called immediately and the result of request
// would not be asign to modelInstance.someProperty
};
I'm using the async library to help me with my control flow. I have a collection over which I want to iterate, for each element execute 1 asynchronous task and when all are done, call the callback.
I've decided to use an async.forEach loop, on each loop I call my asynchronous task but I get an error: callback was already called, but shouldn't the callback be called only when all callbacks are called? And I even wanted to understand properly how to handle errors, it is highly probable that some task will fail and others will succeed, I don't need to know which elements fail, but I would like, how can I do this?
This is my code:
async.forEach(fonts, function(font, callback) {
ftpm_module.installOsFont(font, callback);
}, function() {
console.log("finished");
});
EDIT: the error occurs only if I pass 2 or more fonts.
This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 8 years ago.
I have this source code:
UserSchema.post('save', function (next) {
doSomethingAsync(function(){
next()
});
});
myFunc = function(user){
Q.ninvoke(user, 'save').then(function(){
doThisAtTheEnd()
});
}
But then is called before "doSomethingAsync" calls is callback. How is it possible?!
How can I call "then" after all the saving stuff is done?
Thanks very much
EDIT:
the two functions are in different files, no way nor intention to use a global variable.
From the documentation for Q.ninvoke: https://github.com/kriskowal/q/wiki/API-Reference#qninvokeobject-methodname-args
Calls a Node.js-style method with the given variadic arguments,
returning a promise that is fulfilled if the method calls back with a
result, or rejected if it calls back with an error (or throws one
synchronously)
And looking at mongoose schema.post('save'): http://mongoosejs.com/docs/middleware.html
post middleware are executed after the hooked method and all of its
pre middleware have completed. post middleware do not directly receive
flow control, e.g. no next or done callbacks are passed to it. post
hooks are a way to register traditional event listeners for these
methods.
Which means that there is no next for you to call in the doSomethingAsync. Probably something is internally calling back into the ninvoke.
How about deferers? You could generate a deferer and resolve it. i.e.:
var saveDeferer = Q.defer();
UserSchema.post('save', function (next) {
doSomethingAsync(function(){
saveDeferer.resolve();
});
});
saveDeferer.promise.then( function() { doSomething(); } );
Update after the question edit:
It seems to me that you are trying to use schema.post('save', ... as an eventbus that carries flow variables around. I don't see any direct answer to your edit, other than either use a custom event bus, or do some refactoring so that you can pass the promise references around.
I am using Q promise library for chaining queries. I have post hook that adds to new document new property.
MyModel.create(data)
.then(function(data) {
// <------------------ post hook has not been triggered yet
return MySchema.findById(data._id); // trying to get the new document
})
.then(function(data) {
// <------------------ post hook has not been triggered yet
reply(null, data); // <- my callback
});
.done()
// <------------------ post hook has triggered!!!
After new document has been created post hook not triggering, and in node console I see that it triggers after this chain ends.
How can I ensure post hook triggers before I can return new document?
UPDATE: my postsave hook is asynchronous, and mongoose is not waiting for it. There is no other way to use post hook without EventEmitter.
You need to hold on to the original promise d.resolve() until the post hook finishes.
What I mean by that is actually wait in the MyModel.create method. You return d.promise, but do not resolve it. You wait there until your post hook is done, and then resolve the promise.
Now, how to check if your postsave hook is done? I don't know. Can you give it a callback as a param? So that the posthook can call this method once it's done, and you can resolve your original promise.
The other method is to have your .create method poll or whatever until the posthook is triggered.
Now, as for why it doesn't trigger, also you need to show more of the code. What's in the posthook?