我需要帮助来制定路由器轮询的良好策略。我有一个路由queries/:query_id/results/:result_id
,每当用户执行查询时,我都会转换到该路由。在这个 I 路由中,我需要加载两件事:与此路由关联的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
,只有在查询完成后才转换到此。从那里我可以安全地加载桌子。我对此唯一的问题是结果路由将仅涉及加载结果。结果路由中不会呈现任何组件;仅在表路由中。
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
模型、适配器和序列化程序,用于查询公共时间 API,该 API 将返回特定时区的当前时间。如您所见,我正在存储初始结果,在返回它之前,我已经设置了我的标准 JavaScript 间隔,setInterval()
每 10 秒轮询一次以更新时间。然后我在路线上设置间隔,以便在我们离开时取消它。
(注意:我只将间隔设置为 10 秒,因为我受到每 1 秒🙈轮询一次服务的速率限制,您可以将其设置为您想要检查数据的频率)
我已经编辑了我的示例,以在间隔函数中包含另一种基于查询本身的结果停止轮询的方法。这是一个玩具示例,但您可以对其进行编辑以满足您的需求。
正如我在开头所说,我们依靠余烬数据的工作方式来实现无缝。由于findRecord()
和peekRecord()
始终指向同一记录,因此如果对数据的后续请求包含所需的结果,则无需执行任何特殊操作。
我希望这有帮助!