From b4acee531cd47f0a00d5a84fedc687964cef5017 Mon Sep 17 00:00:00 2001 From: wangqianliang Date: Thu, 15 Aug 2019 21:14:12 +0800 Subject: [PATCH] feature(code/frontend): cancel file blob and directory commits request if outdated --- .../plugins/code/public/sagas/editor.ts | 12 ++++---- .../legacy/plugins/code/public/sagas/file.ts | 29 ++++++++++++------- .../plugins/code/public/utils/saga_utils.ts | 20 +++++++++++++ 3 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 x-pack/legacy/plugins/code/public/utils/saga_utils.ts diff --git a/x-pack/legacy/plugins/code/public/sagas/editor.ts b/x-pack/legacy/plugins/code/public/sagas/editor.ts index 1f6ed5d79f1ce3..e00710d241260b 100644 --- a/x-pack/legacy/plugins/code/public/sagas/editor.ts +++ b/x-pack/legacy/plugins/code/public/sagas/editor.ts @@ -215,12 +215,12 @@ function* handleMainRouteChange(action: Action) { } else { yield put(closePanel(false)); } - } - - yield call(handleFile, repoUri, file, revision); - const commits = yield select((state: RootState) => state.revision.treeCommits[file]); - if (commits === undefined) { - yield put(fetchTreeCommits({ revision, uri: repoUri, path: file })); + yield call(handleFile, repoUri, file, revision); + } else { + const commits = yield select((state: RootState) => state.revision.treeCommits[file]); + if (commits === undefined) { + yield put(fetchTreeCommits({ revision, uri: repoUri, path: file })); + } } } const lastRequestPath = yield select(lastRequestPathSelector); diff --git a/x-pack/legacy/plugins/code/public/sagas/file.ts b/x-pack/legacy/plugins/code/public/sagas/file.ts index 01c1785e9e6ac0..568a2d38c8d144 100644 --- a/x-pack/legacy/plugins/code/public/sagas/file.ts +++ b/x-pack/legacy/plugins/code/public/sagas/file.ts @@ -7,7 +7,7 @@ import { Action } from 'redux-actions'; import { npStart } from 'ui/new_platform'; import Url from 'url'; -import { call, put, select, takeEvery, takeLatest } from 'redux-saga/effects'; +import { call, put, select, takeEvery, takeLatest, cancel } from 'redux-saga/effects'; import { fetchDirectory, @@ -45,6 +45,7 @@ import { import { treeCommitsSelector, currentPathSelector } from '../selectors'; import { repoRoutePattern } from './patterns'; import { FileTree } from '../../model'; +import { singletonRequestSaga } from '../utils/saga_utils'; function* handleFetchRepoTree(action: Action) { try { @@ -154,13 +155,15 @@ function* handleFetchMoreCommits(action: Action) { } } -function* handleFetchTreeCommits(action: Action) { +function* handleFetchTreeCommits(action: Action, signal: AbortSignal, task: any) { try { const path = action.payload!.path; - const commits = yield call(requestCommits, action.payload!, path); + const commits = yield call(requestCommits, action.payload!, path, undefined, undefined, signal); yield put(fetchTreeCommitsSuccess({ path, commits })); } catch (err) { yield put(fetchTreeCommitsFailed(err)); + } finally { + yield cancel(task); } } @@ -168,7 +171,8 @@ function requestCommits( { uri, revision }: FetchRepoPayloadWithRevision, path?: string, loadMore?: boolean, - count?: number + count?: number, + signal?: AbortSignal ) { const pathStr = path ? `/${path}` : ''; let query: any = {}; @@ -182,13 +186,15 @@ function requestCommits( `/api/code/repo/${uri}/history/${encodeURIComponent(revision)}${pathStr}`, { query, + signal, } ); } export async function requestFile( payload: FetchFilePayload, - line?: string + line?: string, + signal?: AbortSignal ): Promise { const { uri, revision, path } = payload; const url = `/api/code/repo/${uri}/blob/${encodeURIComponent(revision)}/${path}`; @@ -197,7 +203,8 @@ export async function requestFile( query.line = line; } const response: Response = await fetch( - npStart.core.http.basePath.prepend(Url.format({ pathname: url, query })) + npStart.core.http.basePath.prepend(Url.format({ pathname: url, query })), + { signal } ); if (response.status >= 200 && response.status < 300) { @@ -244,9 +251,9 @@ export async function requestFile( throw new Error('invalid file type'); } -function* handleFetchFile(action: Action) { +function* handleFetchFile(action: Action, signal: AbortSignal, task: any) { try { - const results = yield call(requestFile, action.payload!); + const results = yield call(requestFile, action.payload!, undefined, signal); if (results.isNotFound) { yield put(setNotFound(true)); yield put(fetchFileFailed(new Error('file not found'))); @@ -258,6 +265,8 @@ function* handleFetchFile(action: Action) { } } catch (err) { yield put(fetchFileFailed(err)); + } finally { + yield cancel(task); } } @@ -273,9 +282,9 @@ function* handleFetchDirs(action: Action) { export function* watchFetchBranchesAndCommits() { yield takeEvery(String(fetchRepoBranches), handleFetchBranches); yield takeEvery(String(fetchRepoCommits), handleFetchCommits); - yield takeLatest(String(fetchFile), handleFetchFile); + yield takeLatest(String(fetchFile), singletonRequestSaga(handleFetchFile)); yield takeEvery(String(fetchDirectory), handleFetchDirs); - yield takeLatest(String(fetchTreeCommits), handleFetchTreeCommits); + yield takeLatest(String(fetchTreeCommits), singletonRequestSaga(handleFetchTreeCommits)); yield takeLatest(String(fetchMoreCommits), handleFetchMoreCommits); } diff --git a/x-pack/legacy/plugins/code/public/utils/saga_utils.ts b/x-pack/legacy/plugins/code/public/utils/saga_utils.ts new file mode 100644 index 00000000000000..d9de73a68dddc3 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/utils/saga_utils.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { take, spawn } from 'redux-saga/effects'; +import { Action } from 'redux-actions'; + +function* cancelRequest(action: Action, abortController: AbortController) { + yield take(action.type); + abortController.abort(); +} + +export const singletonRequestSaga = (saga: any) => + function*(action: Action) { + const abortController = new AbortController(); + const task = yield spawn(cancelRequest, action, abortController); + yield spawn(saga, action, abortController.signal, task); + };