mardi 16 juillet 2019

What is an effective strategy for polling in the router in Emberjs?

I need help on developing a good strategy for polling in the router. I have a route queries/:query_id/results/:result_id that I transition to whenever the user executes a query. In this I route I need to load two things: the result model that is associated with this route and a table object using the url inside the result model. The problem is, if a query is long running I need to poll and ask the server if the query is finished. Only then can I download the table. I'm using ember concurrency to do all my polling and it works great except for a small edge case. This edge case has to do with the fact that If my polling function gets canceled after it is finished and while it downloads the table, then it will get stuck saying "loading the table" because it only triggers the polling when the status of the query is not completed. I'm doing all my data loading in the result route so maybe someone can offer some alternatives to do this.

Relevant code in the result route


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.get("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.
    }
  }
});

Idea

One idea is probably creating a separate route for loading the table i.e queries/:query_id/results/:result_id/:table_id and only transition to this once the query is completed. From there I can safely load the table. Only problem I have with this is that the result route will simply be involved with loading the result. There will be no components that will be render in the result route; only in the table route.




Aucun commentaire:

Enregistrer un commentaire