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

Parallel lifecylce methods #141

Closed
jbadeau opened this issue Nov 8, 2013 · 13 comments
Closed

Parallel lifecylce methods #141

jbadeau opened this issue Nov 8, 2013 · 13 comments
Assignees
Milestone

Comments

@jbadeau
Copy link

jbadeau commented Nov 8, 2013

Hi Brian,

We have split up our wire specs into something like:

settings.js (loads settings from servere once)
services.js (loads navigation data, conditions, links, etc..)
app.js (App, controllers, router, etc...)

In the services.js we have services with async/pomised init methods that load data from the server. we have seen that even if there is no interdependency between components that the service calls are executed in sequence rather than paralllel. This has a very big impact for us when latency is high. In our case getting data from China in Europe. We don't use lazy loading as that would also suffer from sequential loading.

Basically I think if there are no dependencies between componenets in a context than any lifecycle method that returns a promise should be resolved in a context scoped parallel promise.

Is this clear?

@briancavalier
Copy link
Member

Hey @jbadeau! This sounds interesting, and definitely like something that shouldn't be happening in situations where there's no dependency between the components. I wonder if there actually is a dependency, but one that's hard to see, perhaps involving several components in a long dependency chain.

Have you tried to reduce this to a simple test case? I tried the following, and it behaves as I'd expect: it allows both a.hello and b.hello to proceed in parallel. It logs "a" and "b" after 1 second (i.e. it's parallel), rather logging "a" after 1 second and "b" after 2 seconds (which would imply no parallelism):

define('foo', ['when/delay'], function(delay) {
    function Foo() {}
    Foo.prototype.hello = function() {
        return delay(1000, this.name).then(function(name) {
            console.log(name);
        });
    };

    return Foo;
});

define('spec', {
    a: {
        create: 'foo',
        properties: { name: 'a'},
        init: 'hello'
    },
    b: {
        create: 'foo',
        properties: { name: 'b'},
        init: 'hello'
    },
    $plugins: ['wire/debug']
});

If you can get more info, or make a test case (feel free to use the above as a starting point!), I'll be happy to dig in and try to figure out what is going on.

@jbadeau
Copy link
Author

jbadeau commented Nov 8, 2013

Hi Brian,

Yes, I will create small test and if the issue persists I will submit it with the versions of the libs I'm using.

Cheers,
Jose

@briancavalier
Copy link
Member

@jbadeau Thanks, much appreciated!

@jbadeau
Copy link
Author

jbadeau commented Nov 13, 2013

Hi Brian,

OK I was wrong in saying that the modules have no dependencies to each other:

therefore:

Given the wire spec:

define({

    parent : {
        create : {
            module : 'welcome/parent',
            args : {
                $ref : 'child'
            }
        },
        properties : {
            name : 'parent'
        },
        ready : 'init'
    },

    child : {
        create : 'welcome/child',
        properties : {
            name : 'child'
        },
        ready : 'init'
    },

    $plugins : [ 'wire/debug' ]

});

And the modules:

define(['when/delay'], function(delay) {
    function Parent() {}

    Parent.prototype.init = function() {
        return delay(1000, this.name).then(function(name) {
            console.log(name);
        });
    };

    return Parent;

});

define(['when/delay'], function(delay) {
    function Child() {}

    Child.prototype.init = function() {
        return delay(1000, this.name).then(function(name) {
            console.log(name);
        });
    };

    return Child;

});

The resulting output is:

DEBUG (total: 0ms, context: 0ms / 0ms): Context debug debug.js:380
DEBUG (total: 3ms, context: 3ms / 3ms): Context init debug.js:384
child child.js:6
DEBUG (total: 1015ms, context: 1012ms / 1015ms): ready child
Child {name: "child", init: function}

Object {id: "child", spec: Object, path: "child", resolver: Object, proxy: ObjectProxy}
debug.js:418
parent parent.js:6
DEBUG (total: 2021ms, context: 1006ms / 2021ms): ready parent
Parent {name: "parent", init: function}

Object {id: "parent", spec: Object, path: "parent", resolver: Object, proxy: ObjectProxy}
debug.js:418
DEBUG (total: 2022ms, context: 1ms / 2022ms): Context ready debug.js:389
Looking good!

However I expect the output:

...
DEBUG (total: 1015ms, context: 1ms / 1015ms): Context ready debug.js:389
Looking good!

I would expect that returning a promise during the child's init lifecycle hook would force the parent module to wait until the child module was ready. This is what you have proven!

However we would like to maintain the dependency between parent and child but be able to execute async code in the ready lifecycle hook module and have its resolution on the complete wire.

Perhaps this does not make sense is some cases?

Basically in our service spec we allow module dependencies (a service needs a store) but only the app spec modules (child spec) actually use the services. the initialization of a service is not dependent on another service. If it is then we put it in a parent spec.

We want to avoid serial initialization of services.

For the moment we use a wire plugin to call the init method and memoize it so that subsequent calls are cached. It works but of course we would prefer standard solution.

Cheers,
Jose

@briancavalier
Copy link
Member

@jbadeau Thanks for the awesome test case! I'll try it today and let you know what I find.

@briancavalier
Copy link
Member

I think I found the problem here. I'm starting on a fix. Hopefully, I can get it finished over the weekend and then release early next week.

briancavalier added a commit that referenced this issue Nov 21, 2013
…ing. This commit starts to experiment with fixing it
@briancavalier
Copy link
Member

@jbadeau Ok, I think I have a fix. Try the issue-141 branch, and let me know if it allows your use cases to resolve in parallel. If it works for you, I'll get this into a 0.10.4 release soon.

@jbadeau
Copy link
Author

jbadeau commented Nov 21, 2013

Will do ASAP,

@ghost ghost assigned briancavalier Nov 21, 2013
@jbadeau
Copy link
Author

jbadeau commented Nov 22, 2013

Hey,

It looks good for us. very nice speed improvement. It works for n-level dependency graphs as well. I would make this behavior explicitly clear in the docs as I think people would def find it useful.

@jbadeau
Copy link
Author

jbadeau commented Nov 22, 2013

It prob means people need to be more careful about using the "ready" method but its worth the tradeoff.

@briancavalier
Copy link
Member

Awesome, thanks for verifying. Glad to hear that worked for you, and that it gives a speed boost! I'll get this ready to release in 0.10.4.

people need to be more careful about using the "ready" method but its worth the tradeoff

Yeah, this is the reason there are two facets that serve slightly different purposes: init and ready. The init facet is always run before the component is allowed to be dereferenced (via $ref, in a pipeline, etc.). In contrast, the ready facet runs after a component is allowed to be dereferenced--most time, it is the last thing in the startup phase of a component's lifecycle. So, depending on your needs, you can choose one or the other .. or both (they are not mutually exclusive).

I'll add a bit to the docs to try to clarify the distinction.

@briancavalier
Copy link
Member

@jbadeau I added a bit more clarification on init vs ready (see fb5b1b5) in the docs, so that'll go out with 0.10.4 as well.

@jbadeau
Copy link
Author

jbadeau commented Nov 22, 2013

coolio :)

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

2 participants