I'm using the Q module for Node.js in attempts to avoid the "pyramid of doom" in scenarios where I have many steps. For example:
function doTask(task, callback) { Q.ncall(task.step1, task) .then(function(result1){ return Q.ncall(task.step2, task); }) .then(function(result2){ return Q.ncall(task.step3, task); }) .fail(callback).end(); }
Essentially this seems to work; if an error is thrown by any of the task steps, it is passed to the callback (though I would be welcome to improvements, as I am new to node.js promises). However, I have a problem when I need to abort the task-chain early. For example, if result1 is successfully returned I might want to call the callback early and abort the rest, but my attempts to do so are failing...
function doTask(task, callback) { Q.ncall(task.step1, task) .then(function(result1){ if(result1) {// the rest of the task chain is unnecessary console.log('aborting!'); callback(null, result1); return null; } return Q.ncall(task.step2, task); }) .then(function(result2){ console.log('doing step 3...'); return Q.ncall(task.step3, task); }) .fail(callback).end(); }
In this example, I see both "aborting!" and "doing step 3..." printed.
I'm sure I'm merely misunderstanding some basic principles here, so would appreciate any help. Thanks!
EDIT: Also keep in mind that if you want to break out of the chain in your error handler, it needs to return a rejected promise or throw an Error (which will be caught and wrapped in a rejected promise automatically). If you don't return a promise, then wraps the return value in a resolve promise for you.
The promise is resolved by calling resolve() if the promise is fulfilled, and rejected by calling reject() if it can't be fulfilled. Both resolve() and reject() takes one argument - boolean , string , number , array , or an object .
Promise chaining: Promise chaining is a syntax that allows you to chain together multiple asynchronous tasks in a specific order. This is great for complex code where one asynchronous task needs to be performed after the completion of a different asynchronous task.
Promise. any() settles when any of the promises you pass to it fulfill or all of the promises get rejected. It returns a single promise that resolves with the value from the first promise that is fulfilled. If all promises are rejected, then the returned promise is rejected with an AggregateError .
This is a case where you will need to branch, which does mean either nesting or creating a subroutine.
function doTask(task, callback) { return Q.ncall(task.step1, task) .then(function(result1) { if (result1) return result1; return Q.ncall(task.step2, task) .then(function(result2) { return Q.ncall(task.step3, task); }) }) .nodeify(callback) }
Or
function doTask(task, callback) { return Q.ncall(task.step1, task) .then(function(result1) { if (result1) { return result1; } else { return continueTasks(task); } }) .nodeify(callback) } function continueTasks(task) { return Q.ncall(task.step2, task) .then(function(result2) { return Q.ncall(task.step3, task); }) }
Any errors that are thrown within the promise chain will cause the entire stack to be aborted early and control is given to the error-back path. (in this case, the fail() handler) When you detect a certain state which causes you to want to abort the promise chain, then just throw a very specific error, which you trap in the error-back and ignore (if you so choose)
function doTask(task, callback) { Q.ncall(task.step1, task) .then(function(result1){ if(result1 == 'some failure state I want to cause abortion') {// the rest of the task chain is unnecessary console.log('aborting!'); throw new Error('abort promise chain'); return null; } return Q.ncall(task.step2, task); }) .then(function(result2){ console.log('doing step 3...'); return Q.ncall(task.step3, task); }) .fail(function(err) { if (err.message === 'abort promise chain') { // just swallow error because chain was intentionally aborted } else { // else let the error bubble up because it's coming from somewhere else throw err; } }) .end(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With