From 32bd112516fecc8d61a9128159b99868f6719d36 Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Fri, 20 Sep 2019 09:37:43 +0200 Subject: [PATCH] *: abort async fetch on component destroy * Use AbortController to signal fetch to stop when component is destroyed * Remove polling and just schedule a new fetch at the end of the current one when not aborted. --- src/components/log.vue | 121 +++++++++++++++++++----------- src/components/projects.vue | 31 ++++++-- src/components/runs.vue | 57 ++++++++++++--- src/components/runsummary.vue | 40 ++++++++-- src/components/tasksummary.vue | 64 +++++++++++++--- src/util/auth.js | 7 +- src/util/data.js | 130 +++++++++++++++++---------------- src/views/Oauth2.vue | 1 - src/views/Project.vue | 30 +++++++- src/views/User.vue | 30 +++++++- 10 files changed, 364 insertions(+), 147 deletions(-) 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 @@
Error fetching Run: {{ fetchRunError }}
Error fetching Task: {{ fetchTaskError }}
- +
@@ -64,13 +64,29 @@ export default { }, data() { return { + fetchAbort: null, + fetchRunError: null, fetchTaskError: null, + run: null, - task: null, - polling: null + task: null }; }, + watch: { + $route: async function() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + clearTimeout(this.fetchRunSchedule); + clearTimeout(this.fetchTaskSchedule); + + this.fetchAbort = new AbortController(); + + this.fetchRun(); + this.fetchTask(); + } + }, methods: { taskClass(task) { if (task.status == "success") return "is-success"; @@ -81,38 +97,66 @@ 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 = error; this.run = data; + this.scheduleFetchRun(); }, async fetchTask() { - let { data, error } = await fetchTask(this.runid, this.taskid); + let { data, error, aborted } = await fetchTask( + this.runid, + this.taskid, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.fetchTaskError = error; + this.scheduleFetchTask(); return; } this.fetchTaskError = error; this.task = data; + this.scheduleFetchTask(); }, - pollData() { - this.polling = setInterval(() => { - this.fetchTask(); + scheduleFetchRun() { + clearTimeout(this.fetchRunSchedule); + this.fetchRunSchedule = setTimeout(() => { this.fetchRun(); }, 2000); }, + scheduleFetchTask() { + clearTimeout(this.fetchTaskSchedule); + this.fetchTaskSchedule = setTimeout(() => { + this.fetchTask(); + }, 2000); + }, approveTask: approveTask }, created: function() { + this.fetchAbort = new AbortController(); + this.fetchRun(); this.fetchTask(); - this.pollData(); }, beforeDestroy() { - clearInterval(this.polling); + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + clearTimeout(this.fetchRunSchedule); + clearTimeout(this.fetchTaskSchedule); } }; diff --git a/src/util/auth.js b/src/util/auth.js index 6ebcaf0..55e3b8b 100644 --- a/src/util/auth.js +++ b/src/util/auth.js @@ -71,13 +71,16 @@ export async function registerapi(init) { } } -export async function fetch(url, init) { - if (init === undefined) { +export async function fetch(url, init, signal) { + if (!init) { init = {} } if (init.headers === undefined) { init["headers"] = {} } + if (signal) { + init["signal"] = signal; + } let idToken = getIdToken(); if (idToken) { init.headers["Authorization"] = "bearer " + idToken diff --git a/src/util/data.js b/src/util/data.js index 7c8e8b9..1bc92a2 100644 --- a/src/util/data.js +++ b/src/util/data.js @@ -1,10 +1,9 @@ -import { apiurl, loginapi, registerapi } from "@/util/auth"; -import { fetch as authfetch } from "@/util/auth"; import router from "@/router"; +import { apiurl, fetch as authfetch, loginapi, registerapi } from "@/util/auth"; -export async function fetch(url, init) { +export async function fetch(url, init, signal) { try { - let res = await authfetch(url, init) + let res = await authfetch(url, init, signal) if (!res.ok) { if (res.status === 401) { router.push({ name: "login" }) @@ -31,6 +30,9 @@ export async function fetch(url, init) { return { data: await res.json(), error: null } } } catch (e) { + if (e.name == 'AbortError') { + return { data: null, error: null, aborted: true, } + } return { data: null, error: "api call failed: " + e.message } } } @@ -82,17 +84,17 @@ export async function register(username, remotesourcename, remoteloginname, remo } } -export async function fetchCurrentUser() { +export async function fetchCurrentUser(signal) { let path = "/user" - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchOrgMembers(orgref) { +export async function fetchOrgMembers(orgref, signal) { let path = "/orgs/" + orgref + "/members" - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchRuns(group, startRunID, lastrun) { +export async function fetchRuns(group, startRunID, lastrun, signal) { let u = apiurl("/runs"); if (group) { u.searchParams.append("group", group) @@ -104,49 +106,49 @@ export async function fetchRuns(group, startRunID, lastrun) { u.searchParams.append("start", startRunID) } - return await fetch(u) + return await fetch(u, null, signal) } -export async function fetchRun(runid) { - return await fetch(apiurl("/runs/" + runid)); +export async function fetchRun(runid, signal) { + return await fetch(apiurl("/runs/" + runid), null, signal); } -export async function fetchTask(runid, taskid) { - return await fetch(apiurl("/runs/" + runid + "/tasks/" + taskid)) +export async function fetchTask(runid, taskid, signal) { + return await fetch(apiurl("/runs/" + runid + "/tasks/" + taskid), signal) } -export async function fetchUser(username) { +export async function fetchUser(username, signal) { let path = "/users/" + username - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchProjectGroup(projectgroupref) { +export async function fetchProjectGroup(projectgroupref, signal) { let path = "/projectgroups/" + encodeURIComponent(projectgroupref) - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchProjectGroupSubgroups(projectgroupref) { +export async function fetchProjectGroupSubgroups(projectgroupref, signal) { let path = "/projectgroups/" + encodeURIComponent(projectgroupref) path += "/subgroups"; - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchProjectGroupProjects(projectgroupref) { +export async function fetchProjectGroupProjects(projectgroupref, signal) { let path = "/projectgroups/" + encodeURIComponent(projectgroupref) path += "/projects"; - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchProject(ref) { +export async function fetchProject(ref, signal) { let path = "/projects/" + encodeURIComponent(ref) - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchSecrets(ownertype, ref, all) { +export async function fetchSecrets(ownertype, ref, all, signal) { let path if (ownertype == "project") { path = "/projects/" @@ -158,10 +160,10 @@ export async function fetchSecrets(ownertype, ref, all) { if (all) { path += "?tree&removeoverridden"; } - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function fetchVariables(ownertype, ref, all) { +export async function fetchVariables(ownertype, ref, all, signal) { let path if (ownertype == "project") { path = "/projects/" @@ -173,10 +175,10 @@ export async function fetchVariables(ownertype, ref, all) { if (all) { path += "?tree&removeoverridden"; } - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function createOrganization(orgname, visibility) { +export async function createOrganization(orgname, visibility, signal) { let path = "/orgs" let init = { method: "POST", @@ -185,10 +187,10 @@ export async function createOrganization(orgname, visibility) { visibility: visibility, }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function createUserToken(username, tokenname) { +export async function createUserToken(username, tokenname, signal) { let path = "/users/" + username + "/tokens" let init = { method: "POST", @@ -196,18 +198,18 @@ export async function createUserToken(username, tokenname) { token_name: tokenname, }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function deleteUserToken(username, tokenname) { +export async function deleteUserToken(username, tokenname, signal) { let path = "/users/" + username + "/tokens/" + tokenname let init = { method: "DELETE", } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function createUserLinkedAccount(username, remotesourcename, loginname, password) { +export async function createUserLinkedAccount(username, remotesourcename, loginname, password, signal) { let path = "/users/" + username + "/linkedaccounts" let init = { method: "POST", @@ -217,18 +219,18 @@ export async function createUserLinkedAccount(username, remotesourcename, loginn remote_source_login_password: password, }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function deleteLinkedAccount(username, laid) { +export async function deleteLinkedAccount(username, laid, signal) { let path = "/users/" + username + "/linkedaccounts/" + laid let init = { method: "DELETE", } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function restartRun(runid, fromStart) { +export async function restartRun(runid, fromStart, signal) { let path = "/runs/" + runid + "/actions" let init = { method: "PUT", @@ -237,10 +239,10 @@ export async function restartRun(runid, fromStart) { from_start: fromStart }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function cancelRun(runid) { +export async function cancelRun(runid, signal) { let path = "/runs/" + runid + "/actions" let init = { method: "PUT", @@ -248,10 +250,10 @@ export async function cancelRun(runid) { action_type: "cancel" }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function stopRun(runid) { +export async function stopRun(runid, signal) { let path = "/runs/" + runid + "/actions" let init = { method: "PUT", @@ -259,10 +261,10 @@ export async function stopRun(runid) { action_type: "stop" }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function approveTask(runid, taskid) { +export async function approveTask(runid, taskid, signal) { let path = "/runs/" + runid + "/tasks/" + taskid + "/actions" let init = { method: "PUT", @@ -270,20 +272,20 @@ export async function approveTask(runid, taskid) { action_type: "approve" }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function fetchRemoteSources() { +export async function fetchRemoteSources(signal) { let path = "/remotesources" - return await fetch(apiurl(path)); + return await fetch(apiurl(path), null, signal); } -export async function userRemoteRepos(remotesourceid) { +export async function userRemoteRepos(remotesourceid, signal) { let path = "/user/remoterepos/" + remotesourceid - return await fetch(apiurl(path)); + return await fetch(apiurl(path, null, signal)); } -export async function createProjectGroup(parentref, name, visibility) { +export async function createProjectGroup(parentref, name, visibility, signal) { let path = "/projectgroups" let init = { method: "POST", @@ -293,10 +295,10 @@ export async function createProjectGroup(parentref, name, visibility) { visibility: visibility }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function updateProjectGroup(projectgroupref, name, visibility) { +export async function updateProjectGroup(projectgroupref, name, visibility, signal) { let path = "/projectgroups/" + encodeURIComponent(projectgroupref) let init = { method: "PUT", @@ -305,10 +307,10 @@ export async function updateProjectGroup(projectgroupref, name, visibility) { visibility: visibility, }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function createProject(parentref, name, visibility, remotesourcename, remoterepopath) { +export async function createProject(parentref, name, visibility, remotesourcename, remoterepopath, signal) { let path = "/projects" let init = { method: "POST", @@ -320,10 +322,10 @@ export async function createProject(parentref, name, visibility, remotesourcenam repo_path: remoterepopath, }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function updateProject(projectref, name, visibility) { +export async function updateProject(projectref, name, visibility, signal) { let path = "/projects/" + encodeURIComponent(projectref) let init = { method: "PUT", @@ -332,29 +334,29 @@ export async function updateProject(projectref, name, visibility) { visibility: visibility, }) } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function deleteProject(projectref) { +export async function deleteProject(projectref, signal) { let path = "/projects/" + encodeURIComponent(projectref) let init = { method: "DELETE", } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function projectUpdateRepoLinkedAccount(projectref) { +export async function projectUpdateRepoLinkedAccount(projectref, signal) { let path = "/projects/" + encodeURIComponent(projectref) + "/updaterepolinkedaccount" let init = { method: "PUT", } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } -export async function deleteProjectGroup(projectgroupref) { +export async function deleteProjectGroup(projectgroupref, signal) { let path = "/projectgroups/" + encodeURIComponent(projectgroupref) let init = { method: "DELETE", } - return await fetch(apiurl(path), init) + return await fetch(apiurl(path), init, signal) } \ No newline at end of file diff --git a/src/views/Oauth2.vue b/src/views/Oauth2.vue index 339a559..368e44e 100644 --- a/src/views/Oauth2.vue +++ b/src/views/Oauth2.vue @@ -24,7 +24,6 @@ export default { error: null, run: null, code: this.$route.query.code, - polling: null, username: null }; }, diff --git a/src/views/Project.vue b/src/views/Project.vue index 98629ba..6ccb969 100644 --- a/src/views/Project.vue +++ b/src/views/Project.vue @@ -168,15 +168,28 @@ export default { }, data() { return { + fetchAbort: null, + dropdownActive: false, run: null }; }, watch: { $route: async function(route) { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + this.fetchAbort = new AbortController(); + this.run = null; if (route.params.runid) { - let { data, error } = await fetchRun(route.params.runid); + let { data, error, aborted } = await fetchRun( + route.params.runid, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; @@ -199,14 +212,27 @@ export default { } }, created: async function() { + this.fetchAbort = new AbortController(); + if (this.$route.params.runid) { - let { data, error } = await fetchRun(this.$route.params.runid); + let { data, error } = await fetchRun( + this.$route.params.runid, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; } this.run = data; } + }, + beforeDestroy() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } } }; diff --git a/src/views/User.vue b/src/views/User.vue index 75533bf..4f9b5bb 100644 --- a/src/views/User.vue +++ b/src/views/User.vue @@ -164,15 +164,28 @@ export default { }, data() { return { + fetchAbort: null, + dropdownActive: false, run: null }; }, watch: { $route: async function(route) { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } + this.fetchAbort = new AbortController(); this.run = null; + if (route.params.runid) { - let { data, error } = await fetchRun(route.params.runid); + let { data, error, aborted } = await fetchRun( + route.params.runid, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; @@ -204,14 +217,27 @@ export default { } }, created: async function() { + this.fetchAbort = new AbortController(); + if (this.$route.params.runid) { - let { data, error } = await fetchRun(this.$route.params.runid); + let { data, error, aborted } = await fetchRun( + this.$route.params.runid, + this.fetchAbort.signal + ); + if (aborted) { + return; + } if (error) { this.$store.dispatch("setError", error); return; } this.run = data; } + }, + beforeDestroy() { + if (this.fetchAbort) { + this.fetchAbort.abort(); + } } };