【问题标题】:Long running load operations in durandaldurandal 中的长时间运行负载操作
【发布时间】:2013-07-30 06:02:11
【问题描述】:

我正在尝试找出运行长时间运行加载操作的最佳位置是使用 Durandal。

据我所知,加载数据的一般建议是在 ViewModel 的 activate 方法中,这是我通常做的 - 类似:

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

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

我知道如果我不在这里返回承诺,那么绑定通常会出现问题 - 如this question and answer indicates

但是,在 activate 方法中执行长时间运行的加载操作会使应用在加载操作完成时“冻结”。例如,如果我的负载现在是这样的呢?

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);
    });
};

在这种情况下,URL 会更新以反映正在加载的页面,但页面内容仍显示前一页(这就是我所说的“冻结”)。

理想情况下,最好将 URL 更改为新页面,并且页面内容也应该显示新页面(即使该页面的数据尚未返回)。然后,随着每个加载操作返回,页面的相关部分应该在数据绑定到视图模型时更新。

有没有推荐的方法在 Durandal 中实现这一点?

我目前的解决方案是在activate 方法中启动负载,然后在viewAttached 方法中填充数据:

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);
    });
};

似乎可以工作,但我记得在某处读过依赖viewAttached 并不是一个好的解决方案。我也不确定是否存在竞争条件,因为我允许激活继续进行。

还有其他建议吗?

【问题讨论】:

  • 一般来说,对于我的 SPA 应用程序,我会创建一个带有“加载”文本和 gif 的透明 div。这个 div 覆盖在整个页面上,以便用户获得应用程序正在尝试做某事的反馈,但同时他们不应该能够在我的页面上单击其他内容或进行某种数据操作被导航。您可以创建一个公共服务,任何视图都可以在其导航开始之前调用该服务,并在导航结束后删除 div。
  • 谢谢@Yogesh,我做了类似的事情——虽然我尽量避免完整的页面覆盖,因为它们在移动设备上表现不佳。

标签: javascript single-page-application durandal


【解决方案1】:

您不必返回承诺,但在这种情况下,您必须在淘汰绑定中处理此问题,这样您就不会绑定到未定义的元素。您可以尝试摆脱激活中的“返回”,但添加一个指示模型是否仍在加载的属性。像这样的:

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

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

然后,在您的视图中,您可以拥有一个在视图仍在加载时显示的部分,以及一个在加载完成时显示的部分。有点像:

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

【讨论】:

  • 我不确定这是否会阻止绑定无法正确应用的问题。 visible: !isLoading() 不会阻止应用子绑定(if 绑定会这样做,因为子节点已从 dom 中删除)。据我所知,如果您在 activate 调用和 viewAttached 调用之间更新 viewModel 的 observable,就会出现问题。
  • 在重新阅读我的问题时,我的问题似乎不够具体 - 我想知道避免数据绑定问题的最佳方法,而不是最好的方法向用户显示正在发生加载操作。所以现在我对你答案的性质有了更好的理解;所以我会把它标记为正确的!
  • 太棒了!所以我们不需要将承诺返回给引擎。
【解决方案2】:

您使用的是哪个版本的 Durandal?在 Durandal 2.0.0pre 中,您将被允许在 activate 中不返回承诺,以便视图的组合(没有数据)可以立即发生。

您可以考虑将viewModel.one 等重构为一个返回构造函数的模块,这样一、二、三将负责检索自己的数据。这样你前两个电话就不必等待loadThirdDataWhichTakesAges。这在一个、两个、三个相互不严重依赖的情况下是有意义的。

【讨论】:

  • 这里的问题是,如果一个/两个/三个都解决在activateviewAttached 之间的某个位置,你最终会遇到一个破坏绑定的竞争条件。检查我在问题中提到的链接以获得解释。不过感谢您的回答!
【解决方案3】:

供参考;我在 Durandal Google Group 上发布了一个类似的问题(实际上是在询问以这种方式使用 activate 和 viewAttached 是否可行)并得到了 Rob Eisenberg 的回复:

这可能会奏效。问题是淘汰赛会摧毁 如果属性被更新并且元素上的数据绑定 目前不在文档中。这可能会发生,具体取决于 异步代码的时间。因为组合的工作方式 1.x,如果您没有从您的激活函数返回承诺,这将导致问题。它应该在 viewAttached 中工作得更好,但是 根据您的作品的性质,可能会附上视图 到它的父级,但仍然不在文档中。这取决于深度 的组成。因此,如果 你在一个深度组合的模块中有这个。不幸的是,没有 由于淘汰赛行为,在 Durandal 1.x 中以一种干净的方式处理它。在 Durandal 2.x 我们重新设计了合成,因此这个问题是 不存在并且不再需要返回承诺(尽管 你仍然可以做到)。 Durandal 2.0 将在大约两年后发布 周。

【讨论】:

    猜你喜欢
    • 2011-01-10
    • 2011-11-30
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-16
    • 2019-08-03
    相关资源
    最近更新 更多