mithril.js: Exceptions silently absorbed in `thennable` processing.

I have posted in detail on StackOverflow (http://stackoverflow.com/q/26007475/8946), but the short story is that when I make a typo in promise chain code a ReferenceError is thrown, but Mithril silently absorbs it at line 719 (for version 0.1.21) in this code:

function fire() {
    ...    
    thennable(ref, function () {
        state = 1
        fire()
    }, function () {
        state = 2
        fire()
    }, function () {
        try {
            if (state == 1 && typeof fn == 'function') {
                val = fn(val)
            }

            else if (state == 2 && typeof er == 'function') {
                val = er(val)
                state = 1
            }
        } catch (e) {
            /**/console.log("[**] Caught in fire.thennable: %o",e);
            val = e
            return finish()
        }
    ...
}

An example of the mistake in my code is:

function getListData(ctl) {
    ctl.data([]);
    ctl.loading(true);
    return m.request({ method: "GET", url: apiBase, background: true }).then(done,fail);

    function done(rspdta) {
        xctl.loading(false);
        ctl.data(rspdta.Customer);
        m.redraw();
        };

    function fail(rspdta) {
        ctl.loading(false);
        ajaxError(ctl);
        m.redraw();
        throw rspdta;                                                                               // continue error condition
        };
    }

Notice the deliberate xctl.loading(false) in the done function – the script just appears to stop there, but a ReferenceError is thrown. However, nothing is logged.

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Comments: 17 (6 by maintainers)

Most upvoted comments

Hi @ekanna

I think that same link has already been posted a couple of times here before. The gist is of the argument is basically “under the hood async stuff is just callbacks, so why not just use callbacks”. The counter argument to that is that promises, etc let you organize code in better ways. The meta conclusion is that it’s hard to see the benefits of the abstraction layer unless you’ve reached a point where you have enough complexity to benefit from these abstractions.

The thing with abstraction arguments is that people often think they’re arguing for simplicity when they’re really arguing for familiarity.

For example, with callbacks, it looks perfectly reasonable to do this:

request(..., function(data) {
  ctrl.things = data
})

I just got rid of the need for promises AND m.prop by using plain js. But this is not necessarily simpler. Once the code starts growing, it becomes harder and harder to find what things exist in ctrl when and it becomes harder to understand when ctrl.things will be available for other async ctrl members (and vice-versa). Managing code like that is really hard.

It’s hard to see why you would need to do relational stuff if you work in a young startup and you always fetch everything by primary key, but once you have an older codebase, relational questions start to show up everywhere and the last thing you want is for every variable in your relational algebra algorithm to be maybe-nullable-depending-on-network-latency.

Promises give you the ability to organize code in a way that makes the availability of data a lot more obvious, easier to understand and refactor. So in that sense, promises are simple because they decrease complexity as code grows, whereas callbacks are only air-quotes “simple” because it’s familiar but you haven’t been burned by the complexity of code growth yet.