1

我需要帮助来制定一个好的路由器轮询策略。queries/:query_id/results/:result_id每当用户执行查询时,我都会转换到一条路线。在这个我的路线中,我需要加载两件事:result与此路线关联的模型和table使用结果模型中的 url 的对象。问题是,如果查询长时间运行,我需要轮询并询问服务器查询是否完成。只有这样我才能下载表格。我正在使用 ember 并发进行所有轮询,除了一个小的边缘情况外,它工作得很好。这种边缘情况与以下事实有关:如果我的轮询函数在完成后被取消并且在下载表时被取消,那么它会卡住说“正在加载表”,因为它只在查询状态时触发轮询没有完成。表的下载发生在轮询函数内部,但仅在查询完成时进行。我正在结果路径中加载所有数据,所以也许有人可以提供一些替代方案来做到这一点。我还需要提一下,每个表都将显示在单独的选项卡(引导选项卡)中。因此,当我在选项卡之间切换时,我想尽量减少获取表格的次数(为什么我将其推送到商店),因为每个选项卡都是指向新结果路由的链接。

结果路由中的相关代码


import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";
import { reject } from "rsvp";
import { task } from "ember-concurrency";
import { encodeGetParams } from "gtweb-webapp-v2/utils";

export default Route.extend({
  poller: service("poller"),

  fetchResults: task(function*(result_id) {
    try {
      const result = yield this.store.findRecord("result", result_id);

      if (result.status === "COMPLETED") {
        const adapter = this.store.adapterFor("application");
        const queryString = encodeGetParams({ parseValues: true, max: 25 });

        const table = {
          table: yield adapter.fetch(
            `${result._links.table.href}?` + queryString
          )
        };

        // Cache the table that we fetched so that we dont have to fetch again if we come back to this route.
        this.store.push({
          data: [
            {
              id: result_id,
              type: "result",
              attributes: table,
              relationships: {}
            }
          ]
        });

        return true;
      }
    } catch (err) {
      return reject(err);
    }
  }),

  model(params) {
    const result = this.store.peekRecord("result", params.result_id);

    if (!result || result.status !== "COMPLETED") {
      const poller = this.get("poller");
      poller.startTask(this.get("fetchResults"), {
        pollingArgs: [params.result_id],
        onComplete: () => {
          this.refresh(); // If we finish polling or have timeout, refresh the route.
        }
      });
    }

    return result;
  },

  setupController(controller, model) {
    const query = { title: this.modelFor("queries.query").get("title") };
    controller.set("query", query);
    this._super(controller, model);
  },

  actions: {
    willTransition() {
      const poller = this.get("poller");
      poller.abort(); // Task was canceled because we are moving to a new result route.
    }
  }
});

主意

一个想法可能是创建一个单独的路径来加载表queries/:query_id/results/:result_id/:table_id,即只有在查询完成后才转换到这个路径。从那里我可以安全地加载桌子。我唯一遇到的问题是结果路由将只涉及加载结果。在结果路由中将不会呈现任何组件;仅在表路由中。

4

1 回答 1

0

Ola @Luis 感谢您的提问

我不确切知道您要对代码做什么,但是对于您要实现的目标似乎有点复杂,因为您使用的是 ember-data 我们可以显着简化此问题。

我们可以依赖的第一件事是,当您调用peekRecord()findRecord()实际返回一个记录时,该记录将在任何具有相同 ID 的查询更新存储时保持最新。

使用这一知识,我们可以显着简化轮询结构。这是我创建的示例路线:

import Route from '@ember/routing/route';

export default Route.extend({
  async model(params) {
    // this is just a hack for the example and should really be something that
    // is purely determined by the backend
    this.stopTime = (Date.now() / 1000) + 20;

    // get the result but don't return it yet
    let result = await this.store.findRecord('time', params.city);

    // setup my poling interval
    let interval = setInterval(async () => {
      let newResult = await this.store.findRecord('time', params.city);

      // this is where you check the status of your results
      if(newResult.unixtime > this.stopTime) {
        clearInterval(interval);
      }
    }, 10000);

    // save it for later so we can cencel it in the willTransition()
    this.set('interval', interval);

    return result;
  },

  actions: {
    willTransition() {
      let interval = this.get('interval');

      if(interval) {
        clearInterval(interval);
      }
    }
  }
});

我创建了一个time模型、适配器和序列化程序,用于查询公共时间 API,该 API 将返回特定时区的当前时间。如您所见,我正在存储初始结果,在返回它之前,我已经设置了标准 JavaScript 间隔,setInterval()该间隔将每 10 秒轮询一次以更新时间。然后我设置了路线上的间隔,这样我可以在我们离开时取消它。

(注意:我只将间隔设置为 10 秒,因为我受到服务的限制,每 1 秒轮询一次,您可以将其设置为您想要检查数据的频率)

我已经编辑了我的示例,以包含另一种基于间隔函数中查询本身的结果来停止轮询的方法。这是一个玩具示例,但您可以对其进行编辑以满足您的需要。

正如我在一开始所说的那样,我们依靠 ember-data 的工作方式来实现无缝连接。因为findRecord()并且peekRecord()始终指向同一记录,如果对您的数据的后续请求包括您需要的结果,您不需要做任何特别的事情。

我希望这有帮助!

于 2019-07-24T14:23:37.850 回答