在Emberjs中对路由器进行轮询的有效策略是什么?

时间:2019-07-16 14:18:01

标签: ember.js polling ember-concurrency

在为路由器中的轮询制定良好策略方面,我需要帮助。我有一条路由queries/:query_id/results/:result_id,每当用户执行查询时,我都会过渡到该路由。在此路由中,我需要加载两件事:与该路由关联的result模型和使用结果模型中的url的table对象。问题是,如果查询长时间运行,我需要轮询并询问服务器查询是否完成。只有这样我才能下载表格。我正在使用余烬并发来进行所有轮询,除了很小的情况下,它的工作效果很好。这种极端的情况与以下事实有关:如果我的轮询功能在完成后又取消了下载表的过程而被取消,则它会卡住“正在加载表”,因为它仅在查询状态时才触发轮询尚未完成。该表的下载在轮询功能内部进行,但仅在查询完成时进行。我正在结果路由中加载所有数据,因此也许有人可以提供一些替代方法来执行此操作。我还需要提及的是,每个表都将显示在单独的选项卡(引导选项卡)中。因此,当我在标签之间切换时,由于每个标签都是指向新结果路径的链接,因此我希望尽量减少获取表格的时间(为什么要将其推送到商店)。

结果路径中的相关代码


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,并且只有在查询完成后才过渡到此。从那里我可以安全地装载桌子。我对此唯一的问题是结果路由将仅涉及加载结果。结果路线中将不会有任何组件可以渲染。

1 个答案:

答案 0 :(得分:0)

Ola @Luis?谢谢您的提问

我不知道您要使用代码做什么,但是对于您要实现的目标来说似乎有点复杂?因为您使用的是余烬数据,我们可以简化此问题非常重要

我们可以依靠的第一件事是,当您调用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模型,适配器和序列化程序,用于查询a public time API,这将返回特定时区的当前时间。如您所见,我将存储初始结果,然后再返回它,然后我用setInterval()设置了标准的JavaScript间隔,该间隔每10秒轮询一次以更新时间。然后,我在路线上设置间隔,以便我们出发时可以取消它。

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

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

正如我在一开始所说的,我们依靠余烬数据的工作方式来实现无缝连接。由于findRecord()peekRecord()始终指向相同记录,因此,如果对数据的后续请求中包含所需的结果,则无需做任何特殊的事情。

我希望这会有所帮助!