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)