Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nested calls to Chunk.map never return #303

Open
carchrae opened this issue Jul 16, 2013 · 5 comments
Open

nested calls to Chunk.map never return #303

carchrae opened this issue Jul 16, 2013 · 5 comments

Comments

@carchrae
Copy link
Contributor

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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.

@uzquiano
Copy link

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.

@djx314
Copy link

djx314 commented Jan 8, 2017

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

@gilsondelrei
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants