在为路由器中的轮询制定良好策略方面,我需要帮助。我有一条路由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
,并且只有在查询完成后才过渡到此。从那里我可以安全地装载桌子。我对此唯一的问题是结果路由将仅涉及加载结果。结果路线中将不会有任何组件可以渲染。
答案 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()
始终指向相同记录,因此,如果对数据的后续请求中包含所需的结果,则无需做任何特殊的事情。>
我希望这会有所帮助!