Skip to content

Commit 73517b1

Browse files
committed
Cancel old Tracer API when page moves
1 parent f15bad2 commit 73517b1

File tree

11 files changed

+99
-95
lines changed

11 files changed

+99
-95
lines changed

package-lock.json

Lines changed: 0 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
"@fortawesome/fontawesome-svg-core": "^1.2.0",
4646
"@fortawesome/react-fontawesome": "0.1.0",
4747
"autoprefixer": "latest",
48-
"axios-progress-bar": "^1.1.8",
4948
"babel-core": "^6.18.0",
5049
"babel-loader": "^7.1.2",
5150
"babel-plugin-transform-builtin-extend": "^1.1.2",

src/frontend/apis/index.js

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,49 @@
11
import Promise from 'bluebird';
22
import axios from 'axios';
33

4-
axios.interceptors.response.use(
5-
response => response.data,
6-
error => {
7-
const { data } = error.response;
8-
const message = typeof data === 'string' ? data : JSON.stringify(data);
9-
return Promise.reject(new Error(message));
10-
},
11-
);
4+
axios.interceptors.response.use(response => response.data);
125

136
const request = (url, process) => {
147
const tokens = url.split('/');
158
const baseURL = /^https?:\/\//i.test(url) ? '' : '/api';
169
return (...args) => {
17-
return new Promise((resolve, reject) => {
18-
const mappedURL = baseURL + tokens.map((token, i) => token.startsWith(':') ? args.shift() : token).join('/');
19-
return resolve(process(mappedURL, args));
20-
});
10+
const mappedURL = baseURL + tokens.map((token, i) => token.startsWith(':') ? args.shift() : token).join('/');
11+
return Promise.resolve(process(mappedURL, args));
2112
};
2213
};
2314

2415
const GET = URL => {
2516
return request(URL, (mappedURL, args) => {
26-
const [params] = args;
27-
return axios.get(mappedURL, { params });
17+
const [params, cancelToken] = args;
18+
return axios.get(mappedURL, { params, cancelToken });
2819
});
2920
};
3021

3122
const DELETE = URL => {
3223
return request(URL, (mappedURL, args) => {
33-
const [params] = args;
34-
return axios.delete(mappedURL, { params });
24+
const [params, cancelToken] = args;
25+
return axios.delete(mappedURL, { params, cancelToken });
3526
});
3627
};
3728

3829
const POST = URL => {
3930
return request(URL, (mappedURL, args) => {
40-
const [body, params] = args;
41-
return axios.post(mappedURL, body, { params });
31+
const [body, params, cancelToken] = args;
32+
return axios.post(mappedURL, body, { params, cancelToken });
4233
});
4334
};
4435

4536
const PUT = URL => {
4637
return request(URL, (mappedURL, args) => {
47-
const [body, params] = args;
48-
return axios.put(mappedURL, body, { params });
38+
const [body, params, cancelToken] = args;
39+
return axios.put(mappedURL, body, { params, cancelToken });
4940
});
5041
};
5142

5243
const PATCH = URL => {
5344
return request(URL, (mappedURL, args) => {
54-
const [body, params] = args;
55-
return axios.patch(mappedURL, body, { params });
45+
const [body, params, cancelToken] = args;
46+
return axios.patch(mappedURL, body, { params, cancelToken });
5647
});
5748
};
5849

@@ -72,7 +63,6 @@ const GitHubApi = {
7263
forkGist: POST('https://api.github.com/gists/:id/forks'),
7364
};
7465

75-
let jsWorker = null;
7666
const TracerApi = {
7767
md: ({ code }) => Promise.resolve([{
7868
tracerKey: '0-MarkdownTracer-Markdown',
@@ -83,12 +73,23 @@ const TracerApi = {
8373
method: 'set',
8474
args: [code],
8575
}]),
86-
js: ({ code }) => new Promise((resolve, reject) => {
87-
if (jsWorker) jsWorker.terminate();
88-
jsWorker = new Worker('/api/tracers/js');
89-
jsWorker.onmessage = e => resolve(e.data);
90-
jsWorker.onerror = reject;
91-
jsWorker.postMessage(code);
76+
js: ({ code }, params, cancelToken) => new Promise((resolve, reject) => {
77+
const worker = new Worker('/api/tracers/js');
78+
if (cancelToken) {
79+
cancelToken.promise.then(cancel => {
80+
worker.terminate();
81+
reject(cancel);
82+
});
83+
}
84+
worker.onmessage = e => {
85+
worker.terminate();
86+
resolve(e.data);
87+
};
88+
worker.onerror = error => {
89+
worker.terminate();
90+
reject(error);
91+
};
92+
worker.postMessage(code);
9293
}),
9394
cpp: POST('/tracers/cpp'),
9495
java: POST('/tracers/java'),

src/frontend/common/util.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,9 @@ const refineGist = gist => {
2121
return { login, gistId, title, files };
2222
};
2323

24-
const handleError = function (error) {
25-
console.error(error);
26-
this.props.showErrorToast(error.message);
27-
};
28-
2924
export {
3025
classes,
3126
distance,
3227
extension,
3328
refineGist,
34-
handleError,
3529
};

src/frontend/components/App/index.jsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import { Helmet } from 'react-helmet';
66
import AutosizeInput from 'react-input-autosize';
77
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
88
import faPlus from '@fortawesome/fontawesome-free-solid/faPlus';
9-
import { loadProgressBar } from 'axios-progress-bar';
10-
import 'axios-progress-bar/dist/nprogress.css';
119
import {
10+
BaseComponent,
1211
CodeEditor,
1312
Header,
1413
Navigator,
@@ -19,15 +18,13 @@ import {
1918
} from '/components';
2019
import { AlgorithmApi, GitHubApi } from '/apis';
2120
import { actions } from '/reducers';
22-
import { extension, handleError, refineGist } from '/common/util';
21+
import { extension, refineGist } from '/common/util';
2322
import { exts, languages } from '/common/config';
2423
import { SCRATCH_PAPER_MD } from '/files';
2524
import styles from './stylesheet.scss';
2625

27-
loadProgressBar();
28-
2926
@connect(({ current, env }) => ({ current, env }), actions)
30-
class App extends React.Component {
27+
class App extends BaseComponent {
3128
constructor(props) {
3229
super(props);
3330

@@ -51,9 +48,10 @@ class App extends React.Component {
5148

5249
AlgorithmApi.getCategories()
5350
.then(({ categories }) => this.props.setCategories(categories))
54-
.catch(handleError.bind(this));
51+
.catch(this.handleError);
5552

56-
this.props.history.block(() => {
53+
this.props.history.block((location) => {
54+
if (location.pathname === this.props.location.pathname) return;
5755
if (!this.isSaved()) return 'Are you sure want to discard changes?';
5856
});
5957
}
@@ -117,7 +115,7 @@ class App extends React.Component {
117115
});
118116
return paginateGists()
119117
.then(scratchPapers => this.props.setScratchPapers(scratchPapers))
120-
.catch(handleError.bind(this));
118+
.catch(this.handleError);
121119
}
122120

123121
loadAlgorithm({ categoryKey, algorithmKey, gistId }) {
@@ -146,7 +144,7 @@ class App extends React.Component {
146144
};
147145
fetch()
148146
.catch(error => {
149-
handleError.bind(this)(error);
147+
this.handleError(error);
150148
this.props.setHome();
151149
})
152150
.finally(() => {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
3+
class BaseComponent extends React.Component {
4+
constructor(props) {
5+
super(props);
6+
7+
this.handleError = this.handleError.bind(this);
8+
}
9+
10+
handleError(error) {
11+
console.error(error);
12+
if (error.response) {
13+
const { data } = error.response;
14+
const message = typeof data === 'string' ? data : JSON.stringify(data);
15+
this.props.showErrorToast(message);
16+
} else {
17+
this.props.showErrorToast(error.message);
18+
}
19+
}
20+
}
21+
22+
export default BaseComponent;

src/frontend/components/Header/index.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ import faSave from '@fortawesome/fontawesome-free-solid/faSave';
1616
import faFacebook from '@fortawesome/fontawesome-free-brands/faFacebook';
1717
import faStar from '@fortawesome/fontawesome-free-solid/faStar';
1818
import { GitHubApi } from '/apis';
19-
import { classes, handleError, refineGist } from '/common/util';
19+
import { classes, refineGist } from '/common/util';
2020
import { actions } from '/reducers';
2121
import { languages } from '/common/config';
22-
import { Button, Ellipsis, ListItem, Player } from '/components';
22+
import { BaseComponent, Button, Ellipsis, ListItem, Player } from '/components';
2323
import styles from './stylesheet.scss';
2424

2525
@withRouter
2626
@connect(({ current, env }) => ({ current, env }), actions)
27-
class Header extends React.Component {
27+
class Header extends BaseComponent {
2828
handleClickFullScreen() {
2929
if (screenfull.enabled) {
3030
if (screenfull.isFullscreen) {
@@ -80,7 +80,7 @@ class Header extends React.Component {
8080
}
8181
})
8282
.then(this.props.loadScratchPapers)
83-
.catch(handleError.bind(this));
83+
.catch(this.handleError);
8484
}
8585

8686
hasPermission() {
@@ -107,7 +107,7 @@ class Header extends React.Component {
107107
this.props.history.push('/');
108108
})
109109
.then(this.props.loadScratchPapers)
110-
.catch(handleError.bind(this));
110+
.catch(this.handleError);
111111
}
112112
}
113113

src/frontend/components/Player/index.jsx

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import React from 'react';
22
import { connect } from 'react-redux';
3-
import Promise from 'bluebird';
43
import InputRange from 'react-input-range';
4+
import axios from 'axios';
55
import faPlay from '@fortawesome/fontawesome-free-solid/faPlay';
66
import faChevronLeft from '@fortawesome/fontawesome-free-solid/faChevronLeft';
77
import faChevronRight from '@fortawesome/fontawesome-free-solid/faChevronRight';
88
import faPause from '@fortawesome/fontawesome-free-solid/faPause';
99
import faWrench from '@fortawesome/fontawesome-free-solid/faWrench';
10-
import { classes, extension, handleError } from '/common/util';
10+
import { classes, extension } from '/common/util';
1111
import { TracerApi } from '/apis';
1212
import { actions } from '/reducers';
13-
import { Button, ProgressBar } from '/components';
13+
import { BaseComponent, Button, ProgressBar } from '/components';
1414
import styles from './stylesheet.scss';
1515

1616
@connect(({ player }) => ({ player }), actions)
17-
class Player extends React.Component {
17+
class Player extends BaseComponent {
1818
constructor(props) {
1919
super(props);
2020

@@ -24,6 +24,8 @@ class Player extends React.Component {
2424
building: false,
2525
};
2626

27+
this.tracerApiSource = null;
28+
2729
this.reset();
2830
}
2931

@@ -65,17 +67,31 @@ class Player extends React.Component {
6567
}
6668

6769
build(file) {
70+
this.reset();
6871
if (!file) return;
72+
73+
if (this.tracerApiSource) this.tracerApiSource.cancel();
74+
this.tracerApiSource = axios.CancelToken.source();
6975
this.setState({ building: true });
70-
this.reset();
76+
7177
const ext = extension(file.name);
72-
(ext in TracerApi ?
73-
TracerApi[ext]({ code: file.content }) :
74-
Promise.reject(new Error('Language Not Supported')))
75-
.then(traces => this.reset(traces))
76-
.then(() => this.next())
77-
.catch(handleError.bind(this))
78-
.finally(() => this.setState({ building: false }));
78+
if (ext in TracerApi) {
79+
TracerApi[ext]({ code: file.content }, undefined, this.tracerApiSource.token)
80+
.then(traces => {
81+
this.tracerApiSource = null;
82+
this.setState({ building: false });
83+
this.reset(traces);
84+
this.next();
85+
})
86+
.catch(error => {
87+
if (axios.isCancel(error)) return;
88+
this.tracerApiSource = null;
89+
this.setState({ building: false });
90+
this.handleError(error);
91+
});
92+
} else {
93+
this.handleError(new Error('Language Not Supported'));
94+
}
7995
}
8096

8197
isValidCursor(cursor) {

src/frontend/components/VisualizationViewer/index.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from 'react';
22
import { connect } from 'react-redux';
3-
import { classes, handleError } from '/common/util';
4-
import { ResizableContainer } from '/components';
3+
import { classes } from '/common/util';
4+
import { BaseComponent, ResizableContainer } from '/components';
55
import { actions } from '/reducers';
66
import styles from './stylesheet.scss';
77
import { Array1DData, Array2DData, ChartData, Data, GraphData, LogData, MarkdownData } from '/core/datas';
88

99
@connect(({ player }) => ({ player }), actions)
10-
class VisualizationViewer extends React.Component {
10+
class VisualizationViewer extends BaseComponent {
1111
constructor(props) {
1212
super(props);
1313

@@ -82,7 +82,7 @@ class VisualizationViewer extends React.Component {
8282
data[method](...args);
8383
}
8484
} catch (error) {
85-
handleError.bind(this)(error);
85+
this.handleError(error);
8686
}
8787
}
8888

src/frontend/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { default as App } from './App';
2+
export { default as BaseComponent } from './BaseComponent';
23
export { default as Button } from './Button';
34
export { default as CodeEditor } from './CodeEditor';
45
export { default as Divider } from './Divider';

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy