Long running load operations in durandal

主宰稳场 提交于 2019-12-23 09:27:09

问题


I'm trying to work out where the best place to run a long-running load operation is using Durandal.

From what I can tell, the general recommendation for loading data is in the ViewModel's activate method, which is what I usually do - something like:

viewModel.activate = function () {
    var loadPromise = myService.loadData();

    return $.when(loadPromise).then(function (loadedData) {
        viewModel.data(data);
    });
};

I know that if I don't return the promise here, then there's usually problems with the bindings - as this question and answer indicates.

However, executing a long running load operation in the activate method makes the app "freeze" while the load operation completes. For example, what if my load was now something like this?

viewModel.activate = function () {
    // All loads return a promise
    var firstLoad = myService.loadFirstData();
    var secondLoad = myService.loadSecondData();
    var thirdLoad = myService.loadThirdDataWhichTakesAges();

    return $.when(firstLoad, secondLoad, thirdLoad).then(function (one, two, three) {
        viewModel.one(one);
        viewModel.two(two);
        viewModel.three(three);
    });
};

In this scenario, the URL is updated to reflect the page which is being loaded, but the page content still shows the previous page (which is what I mean by "freezes").

Ideally, it would be good if the URL should change to the new page, and the page content should show the new page too (even though the data for that page has not yet been returned). Then, as each load operation returns, the relevant part of the page should be updated when the data is bound into the view model.

Is there a recommended way for achieving this inside Durandal?

My current solution is to kick-off the load in the activate method, and then populate the data in the viewAttached method:

var loadPromise;

viewModel.activate = function () {
    // All loads return a promise
    var firstLoad = myService.loadFirstData();
    var secondLoad = myService.loadSecondData();
    var thirdLoad = myService.loadThirdDataWhichTakesAges();

    loadPromise = $.when(firstLoad, secondLoad, thirdLoad);

    // Don't return the promise - let activation proceed.
};

viewModel.viewAttached = function () {
    $.when(loadPromise).then(function (one, two, three) {
        viewModel.one(one);
        viewModel.two(two);
        viewModel.three(three);
    });
};

It seems to work, but I remember reading somewhere that relying on viewAttached wasn't a good solution. I'm also not sure if there is potential for a race condition since I'm allowing the activate to proceed.

Any other recommendations?


回答1:


You don't have to return a promise but in that case you must handle this in you knockout bindings so you woun't bind to elements that are undefined. You can try to get rid of that 'return' in activate but add a property indicating if model is still loading. Something like this:

viewModel.isLoading = ko.observable(false);
viewModel.activate = function () {
   isLoading(true);
   var loadPromise = myService.loadData();

   $.when(loadPromise).then(function (loadedData) {
      viewModel.data(data);
      isLoading(false);
   });
};

And then, in your view, you can have a section that shows up when view is still loading and one that shows up when loading is done. Sometinhg like:

<div data-bind:"visible: isLoading()">Loading Data....</div>
<div data-bind:"visible: !isLoading()">Put your regular view with bindings here. Loading is done so bindings will work.</div>



回答2:


Which version of Durandal are you using? In Durandal 2.0.0pre you would be allowed NOT returning a promise in activate so that the composition of the view (without data) could happen immediately.

You might consider refactoring viewModel.one etc. into a module that returns a constructor function, so that each one, two, three would be responsible for retrieving their own data. That way you first two calls wouldn't have to wait on loadThirdDataWhichTakesAges. That would make sense in scenarios where one, two, three are not heavily depend on each other.




回答3:


For reference; I posted a similar question on the Durandal Google Group (effectively asking if using activate and viewAttached in this manner is an OK idea) and got this reply from Rob Eisenberg:

That will probably work. The problem is that Knockout will destroy databindings on elements if the properties are updated and the element isn't currently in the document. This can happen depending on the timing of the async code. Because of the way composition worked in 1.x, this would cause problems if you didn't return the promise from your activate function. It should work better in viewAttached, but depending on the nature of your composition, the view may be attached to its parent, but still not in the document. It depends on the depth of the composition. So, you could encounter issues with this too if you have this in a deeply composed module. Unfortunately, there isn't a clean way about it in Durandal 1.x due to the knockout behavior. In Durandal 2.x we have reworked composition so that this problem is non-existent and returning the promise is no longer necessary (though you can still do it). Durandal 2.0 will be releasing in about two weeks.



来源:https://stackoverflow.com/questions/17939344/long-running-load-operations-in-durandal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!