dustjs icon indicating copy to clipboard operation
dustjs copied to clipboard

nested calls to Chunk.map never return

Open carchrae opened this issue 11 years ago • 5 comments

It seems that map does not work when called on an already mapped chunk.

For example, this code will never execute the dust.render callback:

dust.loadSource(dust.compile("test of async mapping {x}", "test"));
var data = {
    x : function(chunk) {
        return chunk.map(function(chunk2) {
            var f2 = function() {
                chunk2.write("hmmm!");
                               // change the next line to chunk.map and it works as expected
                chunk2.map(function(chunk3) {
                    var f3 = function() {
                        chunk3.write("mmm!!");
                        chunk3.end();
                    };
                    setTimeout(f3, 1000);
                });
                chunk2.end();
            };
            setTimeout(f2, 1000);
        });
    }
};
dust.render("test", data, function(err, out) {
    $("body").html(out);
});

Is this a limitation, a bug, or am I doing something horrible?

carchrae avatar Jul 16 '13 23:07 carchrae

also, noted that this behaviour goes back to the original dust.js : http://akdubya.github.io/dustjs/ - you can see it in the 'try it out boxes' if you copy the example above.

carchrae avatar Jul 16 '13 23:07 carchrae

Ok, here is the issue.

Despite the docs saying that a chunk created by map must always call Chunk.end(), it just isn't true if you call map again on the new chunk. The chunk that existed prior to the chunk.map call no longer needs to have Chunk.end() called.

Here is the code working as expected:

dust.loadSource(dust.compile("test of async mapping {x}", "test"));
var data = {
    x : function(chunk) {
        return chunk.map(function(chunk2) {
            var f2 = function() {
                chunk2.write("hmmm!");
                               // change the next line to chunk.map and it works as expected
                chunk2.map(function(chunk3) {
                    var f3 = function() {
                        chunk3.write("mmm!!");
                        chunk3.end();
                    };
                    setTimeout(f3, 1000);
                }).end();  // <- call end on the chunk returned by map, chunk2 does not need to end() anymore!
            };
            setTimeout(f2, 1000);
        });
    }
};
dust.render("test", data, function(err, out) {
    $("body").html(out);
});

I'm going to leave this open because the documentation on map is very poor.

carchrae avatar Jul 17 '13 01:07 carchrae

For the purposes of how we're using chunk.map(), we ended up circumventing this issue for the time being by using a helper function that sets the cursor flushable by default:

var map = function(chunk, callback)
{
    var cursor = chunk.map(function(branch) {
        callback(branch);
    });
    cursor.flushable = true;

    return cursor;
};

And then, instead of:

    return chunk.map(function(chunk) { ... })

You can do:

    return map(chunk, function(chunk) { ... });

The basic issue seems to be related to the map function creating two "child" chunks (cursor and branch). You can end() the branch but the cursor never ends and remains !flushable and so the nested map call returns prematurely and does not successfully trigger down the stack to the Stub. And so the callback(err, out) does not get triggered.

This is by design since the cursor could still be open and something could still be writing to it. For example, you might fork off multiple branches and might be waiting for multiple things to complete before you consider the cursor ready to flush.

In our case, however, we never do this. We simply want a single asynchronous chunk (we make a remote API call) that, once it completes, the whole thing rolls back. So far, this solution seems to work for us.

uzquiano avatar Oct 13 '13 20:10 uzquiano

@uzquiano This helps for me. It works! Thanks a lot.

djx314 avatar Jan 08 '17 00:01 djx314

Is possible use the @uzquiano sample while calling and wait a promises end ? Could you to take a real sample using promises ?

gilsondelrei avatar Jun 20 '17 01:06 gilsondelrei