最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Long running load operations in durandal - Stack Overflow

programmeradmin5浏览0评论

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 remendation 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 pletes. 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 remended 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 remendations?

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 remendation 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 pletes. 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 remended 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 remendations?

Share Improve this question edited May 23, 2017 at 12:00 CommunityBot 11 silver badge asked Jul 30, 2013 at 6:02 gerrodgerrod 6,6676 gold badges36 silver badges49 bronze badges 2
  • In general, for my SPA applications I create a transparent div with "loading" text and gif. This div is overlayed over the plete page, so that users get a feed back that the application is trying to do something but at the same time they should not be able to click on something else or do some kind of data manipulation while my page is being navigated. You can create a mon service which any view can call before its navigation starts and remove the div after the navigation ends. – Yogesh Commented Jul 31, 2013 at 15:24
  • Thanks @Yogesh, I do something similar - though I try to avoid plete page overlays because they don't perform well on mobile devices. – gerrod Commented Aug 1, 2013 at 4:33
Add a ment  | 

3 Answers 3

Reset to default 8

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>

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 position 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.

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 position 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 position, the view may be attached to its parent, but still not in the document. It depends on the depth of the position. So, you could encounter issues with this too if you have this in a deeply posed 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 position 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.

发布评论

评论列表(0)

  1. 暂无评论