1

I have a chain of promises

promisedStep1()
    .then(promisedStep2)
    .then(promisedStep3)
    .then(promisedStep4)
    .then(function (value4) {
        // Do something with value4
    })
    .catch(function (error) {
        // Handle any error from all above steps
    });

And I need to know at which step I got an error.

If I add catch to each promised step

promisedStep1()
    .catch(function(){
        throw {code: 1}
    })
    .then(promisedStep2)
    .catch(function(){
        throw {code: 2}
    })
    .catch(function (error) {
        console.log('error in ', error.code);
    });

code would be always 2, because it goes from first catch to second.

What are the techniques in handling such errors?

Edit

Found a way:

function promisedStep1Wrapper(){
    return promisedStep1()
        .catch(function(){
            throw {code: 1};
        });
}

function promisedStep2Wrapper(){
    return promisedStep2()
        .catch(function(){
            throw {code: 2};
        });
}

promisedStep1Wrapper()
    .then(promisedStep2Wrapper)
    .catch(function(err){
        console.log(err.code);
    });

Is this ok, or is there any better solution?

Artem Svirskyi
  • 7,305
  • 7
  • 31
  • 43
  • 1
    Your edit seems fine – Harpreet Singh Sep 16 '15 at 09:37
  • related (and possible duplicate) questions: [Break promise chain and call a function based on the step in the chain where it is broken](http://stackoverflow.com/q/20714460/1048572), [Skipping promise chain after handling error](http://stackoverflow.com/q/24355960/1048572), [Handling multiple catches in promise chain](http://stackoverflow.com/q/26076511/1048572) – Bergi Sep 16 '15 at 12:26
  • @BenjaminGruenbaum: Do we need to make one of those a canonical? – Bergi Sep 16 '15 at 12:26

1 Answers1

1

Is this ok?

Yes, totally.

or is there any better solution?

"Better" I don't know, but there are others for sure. One approach would be to only handle those errors you expect - the ones from the previous step, and rethrow everything else. Some libraries like Bluebird do have dedicated helper function for this definitely superior approach. It would basically look like this:

promisedStep1() // throws Step1Error
.catch(function(err) {
    if (!(err instanceof Step1Error)) throw err;
    throw {code: 1};
})
.then(promisedStep2) // throws Step2Error
.catch(function(err) {
    if (!(err instanceof Step2Error)) throw err;
    throw {code: 2};
})
.catch(function(error) {
    if (!("code" in error)) throw error; // duck typing
    console.log('error in ', error.code);
})
.catch(function(error) {
    console.error("completely unexpected:", error);
});

Another approach is nesting and using the second then callback for dedicated error handling - see When is .then(success, fail) considered an antipattern for promises? for the difference to .then().catch(). Using this, your code would look like

promisedStep1()
.then(function(res) {
    return promisedStep2(res)
    .then(function(res) {
        // do something
        return res;
    }, function(err) { // error in promisedStep2
        throw {code: 2};
    });
}, function(err) { // error in promisedStep1
    throw {code: 1};
})
.catch(function(error) {
    if ("code" in error)
        console.log('error in ', error.code);
    else
        console.error("completely unexpected:", error); // from "do something"
});

This approach works well and allow fine-grained control over attaching which handlers where, cleanly separating success and error paths, but is a bit syntactically confusing.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • In first approach, for example `promisedStep1` is separate module that throws error of instance `Step1Error`. So its custom error defined inside of this module with constructor `Step1Error` that inherits from `Error`. And to make this check `err instanceof Step1Error` outside of this module we need reference for constructor `Step1Error`, right? Otherwise error would be thrown - `Step1Error is not defined`. – Artem Svirskyi Sep 17 '15 at 15:51
  • Yes, of course you'd need a reference to the constructor. However if libraries/modules define their own `Error` subclass, they typically expose it anyway (e.g. `step1module.Error`). Otherwise, custom errors are mostly used with custom properties, and you can detect those via duck typing (like that `.code` thing in your example). – Bergi Sep 17 '15 at 16:04