Reputation: 73
I've been trying to get into the habit of using promises but ran into problems while trying to use them in server side code in the context of Meteor. This is the problem:
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
p = function(){
return new Promise(function(res,rej) {
res("asd");
});
};
p().then(function(asd){
console.log("asd is " + asd);
return "zxc"
}).then(Meteor.bindEnvironment(function(zxc){
console.log("zxc is " + zxc);
return "qwe"
})).then(function(qwe){
console.log("qwe is " + qwe);
});
});
}
mvrx:bluebird
package is installed
code also available at GitHub
Expected output:
asd is asd
zxc is zxc
qwe is qwe
Actual output:
asd is asd
zxc is zxc
qwe is undefined
Dropping the Meteor.bindEnvironment
wrapper fixes the problem, but i need it in order to be able to use Collections inside the callbacks
So what am i missing here? is it just not possible to use Promises + Meteor this way or is there a bug?
What i'm actually trying to accomplish is parallel pipelines that have important partial results but need a synchronized ending. Something like this.
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
promises = [];
step1 = function(input){
return new Promise(function(res, rej){
console.log(input + ":Step 1");
res(input);
});
};
step2 = function(input){
return new Promise(function(res, rej){
console.log(input + ":Step 2");
res(input);
});
};
step3 = function(input){
return new Promise(function(res, rej){
console.log(input + ":Step 3");
res(input);
});
};
slowIO = function(input){
var inp = input;
return new Promise( function(res,rej){
setTimeout(function(){
console.log(inp + ":SlowIO");
res(inp);
},Math.random()*20000);
});
};
end = function(input){
return new Promise(function(res,rej){
console.log(input + ": done, commiting to database");
res()
});
};
for (var i = 0; i < 100; ++i) {
promises.push(step1("pipeline-" + i).then(step2).then(slowIO).then(step3).then(end));
};
Promise.all(promises).then(function(){
console.log("All complete")
});
});
}
Upvotes: 3
Views: 3268
Reputation: 8865
(Update: I've logged an issue on github to see if it can be resolved.)
Looks like there's a issue with Meteor.bindEnvironment
when used this way.
If it's called from outside a Fiber
it won't return it's value. Notice the missing return
before Fiber(runWithEnvironment).run()
A simple solution for the moment is to return a Promise instead of the result:
// when passed as a callback to `Promise#then`
// allows it to resolve asynchronously
var asyncThen = function(fn){
return function(arg){
return new Promise(function(resolve, reject){
fn(arg, resolve, reject);
})
};
};
Promise.resolve("asd").then(function(asd){
console.log("asd is " + asd);
return "zxc"
}).then(
asyncThen(
Meteor.bindEnvironment(function(zxc, resolve, reject){
console.log("zxc is", zxc);
resolve("qwe");
})
)
).then(function(qwe){
console.log("qwe is " + qwe);
});
Upvotes: 3
Reputation: 4948
A promise is just a way to write async code in a synchronous fashion.
If that's all your after, why not use Meteor.wrapAsync()
? In your case, you've got zxc
riding cowboy in it's own fiber & who knows when it's coming back. Bluebird is great & super fast on the client, but I think the code is much cleaner using what Meteor gives you:
//*UNTESTED*//
asd = function() { return 'foo';};
asdSync = Meteor.wrapAsync(asd);
asdResult = asdSync();
qwe = function(input) {return input.reverse()};
qweSync = Meteor.wrapAsync(qwe);
qweResult = qweSync(asdResult); //should return 'oof'
Upvotes: 1