Not sure if I'm clear enough with this title, but assume that I have a class called Foo with method1
, method2
and method3
. I promisify its methods with promisifyAll
.
Then I have a then-chain and I want to cancel the operation in the middle of the second or first then, and no further then should be called.
I read about Cancellation (http://bluebirdjs.com/docs/api/cancellation.html) but I don't know how to implement it with promisifyAll.
The code I got plus what I need:
var bluebird = require('bluebird');
function Foo() { }
Foo.prototype.method1 = function (cb) {};
Foo.prototype.method2 = function (cb) {};
Foo.prototype.method3 = function (cb) {};
var foo = bluebird.promisifyAll(new Foo());
foo.method1Async()
.then(function (r1) {
// cancel then-chain
res.json("Task stopped");
// just stop right here
promises.cancel();
})
.then(function (r2) {
console.log(r2);
}).then(function (r3) {
console.log(r3);
})
.catch(function (er) {
console.log('Catch!');
});
What is the proper way of having this result?
I know I can just throw something and catch it in the catch
method, but this would make a very big change in my real code.
Try something like this:
var bluebird = require('bluebird');
function Foo() { }
Foo.prototype.method1 = function (cb) { cb(null, 'method1'); };
Foo.prototype.method2 = function (cb) { cb(null, 'method2'); };
Foo.prototype.method3 = function (cb) { cb(null, 'method3'); };
var foo = bluebird.promisifyAll(new Foo());
foo.method1Async()
.then(function (r1) {
console.log('step 1');
// cancel then-chain
console.log("Task stopped");
// just stop right here
return bluebird.reject('some reason');
})
.then(function (r2) {
console.log('step 2');
console.log(r2);
}).then(function (r3) {
console.log('step 3');
console.log(r3);
})
.catch(function (er) {
console.log('Catch!');
console.log('Error:', er);
});
Instead of:
return bluebird.reject('some reason');
you can use:
throw 'some reason';
and the result would be the same but you didn't want to throw errors so you can return a rejected promise instead.
Update 1
But if your intention is to run all 3 methods in series then you will also need to return the next promise at each step with something like this:
var bluebird = require('bluebird');
function Foo() { }
Foo.prototype.method1 = function (cb) { cb(null, 'method1'); };
Foo.prototype.method2 = function (cb) { cb(null, 'method2'); };
Foo.prototype.method3 = function (cb) { cb(null, 'method3'); };
var foo = bluebird.promisifyAll(new Foo());
foo.method1Async()
.then(function (r1) {
console.log('step 1');
console.log('got value:', r1);
// cancel? change to true:
var cancel = false;
if (cancel) {
console.log("Task stopped");
return bluebird.reject('some reason');
} else {
console.log('Keep going');
return foo.method2Async();
}
})
.then(function (r2) {
console.log('step 2');
console.log('got value:', r2);
return foo.method3Async();
}).then(function (r3) {
console.log('step 3');
console.log('got value:', r3);
})
.catch(function (er) {
console.log('Catch!');
console.log('Error:', er);
});
Currently the code in your question would never run any other method than the first one.
Update 2
Another example that doesn't call the last catch
for that case:
foo.method1Async()
.then(function (r1) {
console.log('step 1');
console.log('got value:', r1);
// cancel? change to true:
var cancel = true;
if (cancel) {
console.log("Task stopped");
return bluebird.reject('some reason');
} else {
console.log('Keep going');
return foo.method2Async();
}
})
.then(function (r2) {
console.log('step 2');
console.log('got value:', r2);
return foo.method3Async();
}).then(function (r3) {
console.log('step 3');
console.log('got value:', r3);
})
.catch(function (er) {
if (er === 'some reason') {
return bluebird.resolve('ok');
} else {
return bluebird.reject(er);
}
})
.catch(function (er) {
console.log('Catch!');
console.log('Error:', er);
});
Explanation
Think of it this way: in synchronous code if you had:
r1 = fun1();
r2 = fun2();
r3 = fun3();
then the only way for fun1 to cancel the execution of fun2 and fun3 would be to throw an exception. And similarly for promises, the only way that one then
handler could cancel the execution of the next then
handler is to return a rejected promise. And just like with synchronous code that thrown exception would be caught by the catch
block, here that rejected promise would be passed to the catch
handler.
With synchronous code you can have an internal try/catch
that catches the exception and rethrows it if it isn't the specific one that you used to cancel your execution. With promises you can have an earlier catch
handler that does essentially the same.
This is what happens with the code in Update 2. The rejection reason is compared to some value (which is 'some reason'
in that example) and if it is equal then a resolved promise is returned and so the next catch
handler is not called. If it is not equal then the rejection reason is returned again as a rejected promise witch is then passed to the next catch
handler as a "real" error that you want that last catch
handler to handle.