diff --git a/src/components/log.vue b/src/components/log.vue index e079679..87efb3e 100644 --- a/src/components/log.vue +++ b/src/components/log.vue @@ -29,6 +29,8 @@ export default { formatter.use_classes = true; return { + fetchAbort: null, + items: [], lastitem: "", lines: [], @@ -62,75 +64,106 @@ export default { path += "&follow"; } - let res = await fetch(apiurl(path)); - if (res.status == 200) { - const reader = res.body.getReader(); + try { + this.fetching = true; + let res = await fetch(apiurl(path), { signal: this.fetchAbort.signal }); + if (res.status == 200) { + const reader = res.body.getReader(); - let lastline = ""; - let j = 0; - for (;;) { - let { done, value } = await reader.read(); - if (done) { - return; - } - - let data = new TextDecoder("utf-8").decode(value, { stream: true }); - - let part = ""; - for (var i = 0; i < data.length; i++) { - let c = data.charAt(i); - if (c == "\r") { - // replace lastline from start, simulating line feed (go to start of line) - // this isn't perfect since the previous line contents could have - // been written using different colors and this will lose them but - // in practically all cases this won't happen - lastline = - lastline.slice(0, j) + part + lastline.slice(j + part.length); - j = 0; - this.lastitem = this.formatter.ansi_to_html(lastline); - part = ""; - } else if (c == "\n") { - lastline = - lastline.slice(0, j) + part + lastline.slice(j + part.length); - j += part.length; - this.lastitem = this.formatter.ansi_to_html(lastline); - this.items.push(this.lastitem); - this.lastitem = ""; - lastline = ""; - j = 0; - part = ""; - } else { - part += c; + let lastline = ""; + let j = 0; + for (;;) { + let { done, value } = await reader.read(); + if (done) { + this.fetching = false; + return; } + + let data = new TextDecoder("utf-8").decode(value, { stream: true }); + + let part = ""; + for (var i = 0; i < data.length; i++) { + let c = data.charAt(i); + if (c == "\r") { + // replace lastline from start, simulating line feed (go to start of line) + // this isn't perfect since the previous line contents could have + // been written using different colors and this will lose them but + // in practically all cases this won't happen + lastline = + lastline.slice(0, j) + part + lastline.slice(j + part.length); + j = 0; + this.lastitem = this.formatter.ansi_to_html(lastline); + part = ""; + } else if (c == "\n") { + lastline = + lastline.slice(0, j) + part + lastline.slice(j + part.length); + j += part.length; + this.lastitem = this.formatter.ansi_to_html(lastline); + this.items.push(this.lastitem); + this.lastitem = ""; + lastline = ""; + j = 0; + part = ""; + } else { + part += c; + } + } + lastline = + lastline.slice(0, j) + part + lastline.slice(j + part.length); + j += part.length; + this.lastitem = this.formatter.ansi_to_html(lastline); } - lastline = - lastline.slice(0, j) + part + lastline.slice(j + part.length); - j += part.length; - this.lastitem = this.formatter.ansi_to_html(lastline); } + } catch (e) { + // TODO(sgotti) show that log fetching has failed } + this.fetching = false; + }, + abortFetch() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + this.fetchAbort = new AbortController(); } }, watch: { show: function(post, pre) { if (pre == false && post == true) { + this.abortFetch(); this.fetch(); } + if (pre == true && post == false) { + this.abortFetch(); + } }, - stepphase: function(post, pre) { - if (pre == "notstarted" && post == "running") { + stepphase: function(post) { + if (!this.show) { + return; + } + if (this.fetching) { + return; + } + if (post == "running") { + this.abortFetch(); this.getLogs(true); } else { + this.abortFetch(); this.getLogs(false); } } }, created: function() { + this.fetchAbort = new AbortController(); + if (this.show) { this.fetch(); } }, beforeDestroy() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + if (this.es !== null) { this.es.close(); } diff --git a/src/components/projects.vue b/src/components/projects.vue index ad5c6f2..531f6e0 100644 --- a/src/components/projects.vue +++ b/src/components/projects.vue @@ -59,16 +59,21 @@ export default { }, data() { return { + fetchAbort: null, + fetchProjectGroupsLoading: false, fetchProjectsLoading: false, projects: [], - projectgroups: [], - polling: null + projectgroups: [] }; }, watch: { $route: async function() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + this.fetchAbort = new AbortController(); this.fetchProjects(this.ownertype, this.ownername); this.fetchProjectGroups(this.ownertype, this.ownername); } @@ -101,10 +106,14 @@ export default { } this.startFetchProjectsLoading(); - let { data, error } = await fetchProjectGroupProjects( - projectgroupref.join("/") + let { data, error, aborted } = await fetchProjectGroupProjects( + projectgroupref.join("/"), + this.fetchAbort.signal ); this.stopFetchProjectsLoading(); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; @@ -117,10 +126,14 @@ export default { projectgroupref.push(...this.projectgroupref); } this.startFetchProjectGroupsLoading(); - let { data, error } = await fetchProjectGroupSubgroups( - projectgroupref.join("/") + let { data, error, aborted } = await fetchProjectGroupSubgroups( + projectgroupref.join("/"), + this.fetchAbort.signal ); this.stopFetchProjectGroupsLoading(); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; @@ -131,8 +144,14 @@ export default { projectGroupLink: projectGroupLink }, created: function() { + this.fetchAbort = new AbortController(); this.fetchProjects(this.ownertype, this.ownername); this.fetchProjectGroups(this.ownertype, this.ownername); + }, + beforeDestroy() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } } }; diff --git a/src/components/runs.vue b/src/components/runs.vue index c6d9bfa..1c8f918 100644 --- a/src/components/runs.vue +++ b/src/components/runs.vue @@ -125,13 +125,17 @@ export default { data() { return { now: moment(), + + fetchAbort: null, + fetchRunsLoading: false, fetchRunsLoadingTimeout: false, fetchRunsError: null, + fetchRunsSchedule: null, + runs: null, wantedRunsNumber: 25, hasMoreRuns: false, - polling: null, project: null, user: null }; @@ -163,22 +167,32 @@ export default { return run.tasks_waiting_approval.length > 0; }, update() { - clearInterval(this.polling); + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + clearTimeout(this.fetchRunsSchedule); if (this.projectref !== undefined) { this.fetchProject(); } else { this.fetchUser(); } - this.pollData(); }, async fetchProject() { + this.fetchAbort = new AbortController(); + let projectref = [ this.ownertype, this.ownername, ...this.projectref ].join("/"); - let { data, error } = await fetchProject(projectref); + let { data, error, aborted } = await fetchProject( + projectref, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; @@ -188,7 +202,15 @@ export default { this.fetchRuns(true); }, async fetchUser() { - let { data, error } = await fetchUser(this.ownername); + this.fetchAbort = new AbortController(); + + let { data, error, aborted } = await fetchUser( + this.ownername, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; @@ -227,10 +249,20 @@ export default { if (loading) this.startFetchRunsLoading(); while (!stopFetch) { - let { data, error } = await fetchRuns(group, startRunID, lastrun); + let { data, error, aborted } = await fetchRuns( + group, + startRunID, + lastrun, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.stopFetchRunsLoading(); this.fetchRunsError = error; + + this.scheduleFetchRuns(); return; } this.fetchRunsError = null; @@ -247,10 +279,12 @@ export default { this.stopFetchRunsLoading(); this.runs = newRuns; this.hasMoreRuns = hasMoreRuns; + + this.scheduleFetchRuns(); }, - pollData() { - clearInterval(this.polling); - this.polling = setInterval(() => { + scheduleFetchRuns() { + clearTimeout(this.fetchRunsSchedule); + this.fetchRunsSchedule = setTimeout(() => { this.fetchRuns(); }, 2000); }, @@ -293,7 +327,10 @@ export default { this.update(); }, beforeDestroy() { - clearInterval(this.polling); + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + clearTimeout(this.fetchRunsSchedule); } }; diff --git a/src/components/runsummary.vue b/src/components/runsummary.vue index 84dfdf8..fe3dab8 100644 --- a/src/components/runsummary.vue +++ b/src/components/runsummary.vue @@ -68,9 +68,11 @@ export default { }, data() { return { + fetchAbort: null, + fetchRunError: null, + fetchRunSchedule: null, run: null, - polling: null, taskWidth: 200, taskHeight: 40, @@ -81,6 +83,18 @@ export default { tasksDisplay: "graph" }; }, + watch: { + $route: async function() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + clearTimeout(this.fetchRunSchedule); + + this.fetchAbort = new AbortController(); + + this.fetchRun(); + } + }, methods: { runTaskLink(task) { if (this.projectref) { @@ -108,9 +122,17 @@ export default { return "unknown"; }, async fetchRun() { - let { data, error } = await fetchRun(this.runid); + let { data, error, aborted } = await fetchRun( + this.runid, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.fetchRunError = error; + + this.scheduleFetchRun(); return; } this.fetchRunError = null; @@ -127,19 +149,25 @@ export default { taskID ); } + + this.scheduleFetchRun(); }, - pollData() { - this.polling = setInterval(() => { + scheduleFetchRun() { + clearTimeout(this.fetchRunSchedule); + this.fetchRunSchedule = setTimeout(() => { this.fetchRun(); }, 2000); } }, created: function() { + this.fetchAbort = new AbortController(); this.fetchRun(); - this.pollData(); }, beforeDestroy() { - clearInterval(this.polling); + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + clearTimeout(this.fetchRunSchedule); } }; diff --git a/src/components/tasksummary.vue b/src/components/tasksummary.vue index 4ffc6fd..d624c74 100644 --- a/src/components/tasksummary.vue +++ b/src/components/tasksummary.vue @@ -8,7 +8,7 @@