0

If we have a promise like the following, what are the answers to the questions in its comments?

p.then(function ok () {
    // Can we get err() to trigger from inside here?
}, function err () {
    // Can we get ok() to trigger from inside here?
});

I know that one can attach a new then which can wait for the results or reverse the results of p, but I'm wondering, assuming that p is constant, whether the conditions can call each other recursively also (and without assigning the functions to variables and invoking them by name)...

UPDATE

My use case is as follows.

In IndexedDB, when opening a database, you can listen for the following:

db.onsuccess = ...
db.onerror = ...
db.onblocked = ...

db.js, a library I'm expanding to meet my needs, adds these events with a Promise API, such that a success will resolve the promise, and errors or blocking will reject it.

A common use case for listening for blocking would be to close the database connection which is causing the blocking and IndexedDB will thereupon automatically call onsuccess. The problem is that if we treat onblocked as a rejection, it apparently has no way to retrigger the resolve condition (i.e., the onsuccess). In order to get around this, I can have the blocking be supplied instead as a callback, but I was wondering whether there were any way to do it exclusively using the Promises approach since technically, the error would no longer be an error and would like to give a chance for the original resolve callback to resume with its handling of success.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Brett Zamir
  • 14,034
  • 6
  • 54
  • 77
  • Hey Brett - I deleted my answer as it was no longer pertinent once you updated. As such, I deleted a response, the following: This is a matter of handler ! after handling your error and returning something, if you chain your promise you will return in the cycle of (resolve,reject) – Anonymous0day 1 min ago – james emanon Oct 31 '15 at 02:39
  • Sorry for the confusion. Your comment will work for the average use case, but I want to know if I can avoid the redundancy of adding to the chain when I only need to conditionally recover back to "ok" from an error (or vice versa). – Brett Zamir Oct 31 '15 at 02:44
  • your question is a good one, I hope somebody answers it - I am curious too. – james emanon Oct 31 '15 at 02:47
  • Not very 'understandable', but once we understand [this answer](http://stackoverflow.com/a/33045163/2037556) could be useful ! – Anonymous0day Oct 31 '15 at 02:47

2 Answers2

2

Because you're specifying the error handler as a second parameter to .then, what it semantically means is that it will only trigger if the promise "p" got rejected. So the two paths are mutually exclusive. It also means that if inside your "ok" handler you throw an error, it will NOT be caught by the "err" function.

By contrast, if your code used a .catch, it would catch both an error bubbling up from the promise's rejection, and from within the success handler. So if you had this:

p.then(function () {
    throw new Error("Within the .then");
}).catch(function (error) {
    console.log(error);
});

It would always log the error to the console, regardless of whether p had resolved or rejected.

So for your first question: the ok handler could trigger err function, IF instead of doing .then with two arguments you do .then(successHandler).catch(errorHandler). But for the second question it's "no" no matter what -- there is no logical path from either an error handler or a "catch" to a path that was explicitly skipped due to the rejection.

Update based on updated question description:

Assuming that you get some sort of indicator in the "catch" on why the error occurred (and whether you should really reject or whether you should continue as if it's a success), there's no reason why you couldn't call the same function as you would for success. It's just that it would have two entry points:

p.then(successHandler, failureHandler)
 .then( /* other stuff if you want to chain */ )
 .catch(function(e) {
    // catch-all handler just in case
 })

function successHandler(data) {
    // Do something. Note that to avoid 
    // "breaking the promise chain", the code here
    // should either be all-synchronous or return another promise.
}

function failureHandler(error) {
    if (error.wasDueToBlocking()) {
        return successHandler(error.partialData);
    } else {
        // handle the true error. Again, to avoid 
        // "breaking the promise chain", the code here
        // should either be all-synchronous or return another promise.
    }
}

Update 2 based on requirement to not create standalone named function/variables

I'm not entirely sure why you wouldn't want named functions, and the approach I'm about to show is perhaps a little strange. But conceivably you could do this, instead, with the success handler not doing anything other than chaining through to an actual common handler.

p.then(function(data) {
    return {goOn: true, data: data};
 }, function(error) {
    if (error.wasDueToBlocking()) {
        return {goOn: true, data: error.partialData};
    } else {
        // Do some errorHandling logic
        return {goOn: false};
    }
 }
 .then(function(passedThroughData) {
    if (passedThroughData.goOn) {
        // Do something with it.
        // Otherwise ignore, since error handler
        // already took care of whatever it needed above
    }
 })
 .then(function() {
    // whatever you wanted to happen next
 })
 .catch(function(e) {
    // catch-all handler just in case
 })
  • +1 as that is valid to my intended meaning, and yes, I follow that an added `catch` (as a special variant of `then`) could get triggered. – Brett Zamir Oct 31 '15 at 02:52
  • As far as semantics though, I am working with an IndexedDB API which uses promises for establishing db connections, and the API currently invokes a promise rejection when encountering either errors or blocking events. In the case of blocking, if the user closes the database in the blocking (rejection) branch, they would ideally be able to recover and in such a case, I think it would make sense to go back to resolving the original promise affirmatively (whereas if it were a normal error, it could continue the chain normally). – Brett Zamir Oct 31 '15 at 02:54
  • @BrettZamir are you looking for recursive promise ? – Anonymous0day Oct 31 '15 at 03:01
  • @BrettZamir, see my update to the answer based on your updated use case – Michael Zlatkovsky - Microsoft Oct 31 '15 at 17:25
  • Sorry, I was looking for an alternative to avoiding this, as per the words, "without assigning the functions to variables and invoking them by name". (I mean to include normal function declarations as well but mentioned "variables" since referring to "named functions" might have been confusing since I had already named the inline functions.) – Brett Zamir Oct 31 '15 at 17:34
  • It seems maybe that promises are simply not meant to work like handlers which can be triggered multiple times and as needed. Maybe it should be added to the cases of anti-patterns such as discussed at http://taoofcode.net/promise-anti-patterns/ . Still, I think it would be convenient to be able to refer to the other branch. – Brett Zamir Oct 31 '15 at 17:41
  • See my Update 2 based on requirement to not create standalone named function/variables. Though yes, I think this gets more into the anti-pattern case... But for academic curiosity, here goes :-) – Michael Zlatkovsky - Microsoft Oct 31 '15 at 17:53
  • Appreciate the effort, and your answer has some similarities to the accepted answer, but, besides it being more succinct, I think I do need a new promise because I don't have the actual data yet upon the blocking event. – Brett Zamir Oct 31 '15 at 18:51
2

Can we get err() to trigger from ok?
Can we get ok() to trigger from err?

No. The promises spec mandates that at most one of the two then handlers gets called. If the promise fulfills the first gets called, if the promise rejects the second will, and a promise can't do both things.

So while you could easily create two named functions and call each other manually, it is not programmatically possible to trigger the promise into calling the other.

My use case is as follows […]

What you actually seem to be looking for is

db.open().catch(function(err) {
    if (err.isBlocking)
        return db.closeAndWaitForNextSuccess(err.blocker); // get another promise
    else
        throw err;
}).then(function ok(res) {
    …
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • With your example, I realized I could simplify it further by returning a new promise, "resume", as part of the original `err` object so the user can just add `return err.resume` after closing existing connections. This promise, if used, will overwrite the existing `onsuccess` and `onerror` (which will not work to resolve/reject anymore due to `onblocked` having already triggered a rejection) so that `onsuccess` will open the db using the original parameters. I need to overwrite as opposed to creating a new request since the old request can still fire whatever `onsuccess` is attached to it. – Brett Zamir Oct 31 '15 at 19:39
  • 1
    @BrettZamir: Jupp, that sounds like a nifty pattern. – Bergi Oct 31 '15 at 19:41