提交 73517b10 编写于 作者: J Jason Park

Cancel old Tracer API when page moves

上级 f15bad25
......@@ -690,12 +690,6 @@
"is-buffer": "^1.1.5"
}
},
"axios-progress-bar": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/axios-progress-bar/-/axios-progress-bar-1.2.0.tgz",
"integrity": "sha512-PEgWb/b2SMyHnKJ/cxA46OdCuNeVlo8eqL0HxXPtz+6G/Jtpyo49icPbW+jpO1wUeDEjbqpseMoCyWxESxf5pA==",
"dev": true
},
"axobject-query": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.1.tgz",
......
import Promise from 'bluebird';
import axios from 'axios';
axios.interceptors.response.use(
response => response.data,
error => {
const { data } = error.response;
const message = typeof data === 'string' ? data : JSON.stringify(data);
return Promise.reject(new Error(message));
},
);
axios.interceptors.response.use(response => response.data);
const request = (url, process) => {
const tokens = url.split('/');
const baseURL = /^https?:\/\//i.test(url) ? '' : '/api';
return (...args) => {
return new Promise((resolve, reject) => {
const mappedURL = baseURL + tokens.map((token, i) => token.startsWith(':') ? args.shift() : token).join('/');
return resolve(process(mappedURL, args));
});
return Promise.resolve(process(mappedURL, args));
};
};
const GET = URL => {
return request(URL, (mappedURL, args) => {
const [params] = args;
return axios.get(mappedURL, { params });
const [params, cancelToken] = args;
return axios.get(mappedURL, { params, cancelToken });
});
};
const DELETE = URL => {
return request(URL, (mappedURL, args) => {
const [params] = args;
return axios.delete(mappedURL, { params });
const [params, cancelToken] = args;
return axios.delete(mappedURL, { params, cancelToken });
});
};
const POST = URL => {
return request(URL, (mappedURL, args) => {
const [body, params] = args;
return axios.post(mappedURL, body, { params });
const [body, params, cancelToken] = args;
return axios.post(mappedURL, body, { params, cancelToken });
});
};
const PUT = URL => {
return request(URL, (mappedURL, args) => {
const [body, params] = args;
return axios.put(mappedURL, body, { params });
const [body, params, cancelToken] = args;
return axios.put(mappedURL, body, { params, cancelToken });
});
};
const PATCH = URL => {
return request(URL, (mappedURL, args) => {
const [body, params] = args;
return axios.patch(mappedURL, body, { params });
const [body, params, cancelToken] = args;
return axios.patch(mappedURL, body, { params, cancelToken });
});
};
......@@ -72,7 +63,6 @@ const GitHubApi = {
forkGist: POST('https://api.github.com/gists/:id/forks'),
};
let jsWorker = null;
const TracerApi = {
md: ({ code }) => Promise.resolve([{
tracerKey: '0-MarkdownTracer-Markdown',
......@@ -83,12 +73,23 @@ const TracerApi = {
method: 'set',
args: [code],
}]),
js: ({ code }) => new Promise((resolve, reject) => {
if (jsWorker) jsWorker.terminate();
jsWorker = new Worker('/api/tracers/js');
jsWorker.onmessage = e => resolve(e.data);
jsWorker.onerror = reject;
jsWorker.postMessage(code);
js: ({ code }, params, cancelToken) => new Promise((resolve, reject) => {
const worker = new Worker('/api/tracers/js');
if (cancelToken) {
cancelToken.promise.then(cancel => {
worker.terminate();
reject(cancel);
});
}
worker.onmessage = e => {
worker.terminate();
resolve(e.data);
};
worker.onerror = error => {
worker.terminate();
reject(error);
};
worker.postMessage(code);
}),
cpp: POST('/tracers/cpp'),
java: POST('/tracers/java'),
......
......@@ -21,15 +21,9 @@ const refineGist = gist => {
return { login, gistId, title, files };
};
const handleError = function (error) {
console.error(error);
this.props.showErrorToast(error.message);
};
export {
classes,
distance,
extension,
refineGist,
handleError,
};
......@@ -6,9 +6,8 @@ import { Helmet } from 'react-helmet';
import AutosizeInput from 'react-input-autosize';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import faPlus from '@fortawesome/fontawesome-free-solid/faPlus';
import { loadProgressBar } from 'axios-progress-bar';
import 'axios-progress-bar/dist/nprogress.css';
import {
BaseComponent,
CodeEditor,
Header,
Navigator,
......@@ -19,15 +18,13 @@ import {
} from '/components';
import { AlgorithmApi, GitHubApi } from '/apis';
import { actions } from '/reducers';
import { extension, handleError, refineGist } from '/common/util';
import { extension, refineGist } from '/common/util';
import { exts, languages } from '/common/config';
import { SCRATCH_PAPER_MD } from '/files';
import styles from './stylesheet.scss';
loadProgressBar();
@connect(({ current, env }) => ({ current, env }), actions)
class App extends React.Component {
class App extends BaseComponent {
constructor(props) {
super(props);
......@@ -51,9 +48,10 @@ class App extends React.Component {
AlgorithmApi.getCategories()
.then(({ categories }) => this.props.setCategories(categories))
.catch(handleError.bind(this));
.catch(this.handleError);
this.props.history.block(() => {
this.props.history.block((location) => {
if (location.pathname === this.props.location.pathname) return;
if (!this.isSaved()) return 'Are you sure want to discard changes?';
});
}
......@@ -117,7 +115,7 @@ class App extends React.Component {
});
return paginateGists()
.then(scratchPapers => this.props.setScratchPapers(scratchPapers))
.catch(handleError.bind(this));
.catch(this.handleError);
}
loadAlgorithm({ categoryKey, algorithmKey, gistId }) {
......@@ -146,7 +144,7 @@ class App extends React.Component {
};
fetch()
.catch(error => {
handleError.bind(this)(error);
this.handleError(error);
this.props.setHome();
})
.finally(() => {
......
import React from 'react';
class BaseComponent extends React.Component {
constructor(props) {
super(props);
this.handleError = this.handleError.bind(this);
}
handleError(error) {
console.error(error);
if (error.response) {
const { data } = error.response;
const message = typeof data === 'string' ? data : JSON.stringify(data);
this.props.showErrorToast(message);
} else {
this.props.showErrorToast(error.message);
}
}
}
export default BaseComponent;
......@@ -16,15 +16,15 @@ import faSave from '@fortawesome/fontawesome-free-solid/faSave';
import faFacebook from '@fortawesome/fontawesome-free-brands/faFacebook';
import faStar from '@fortawesome/fontawesome-free-solid/faStar';
import { GitHubApi } from '/apis';
import { classes, handleError, refineGist } from '/common/util';
import { classes, refineGist } from '/common/util';
import { actions } from '/reducers';
import { languages } from '/common/config';
import { Button, Ellipsis, ListItem, Player } from '/components';
import { BaseComponent, Button, Ellipsis, ListItem, Player } from '/components';
import styles from './stylesheet.scss';
@withRouter
@connect(({ current, env }) => ({ current, env }), actions)
class Header extends React.Component {
class Header extends BaseComponent {
handleClickFullScreen() {
if (screenfull.enabled) {
if (screenfull.isFullscreen) {
......@@ -80,7 +80,7 @@ class Header extends React.Component {
}
})
.then(this.props.loadScratchPapers)
.catch(handleError.bind(this));
.catch(this.handleError);
}
hasPermission() {
......@@ -107,7 +107,7 @@ class Header extends React.Component {
this.props.history.push('/');
})
.then(this.props.loadScratchPapers)
.catch(handleError.bind(this));
.catch(this.handleError);
}
}
......
import React from 'react';
import { connect } from 'react-redux';
import Promise from 'bluebird';
import InputRange from 'react-input-range';
import axios from 'axios';
import faPlay from '@fortawesome/fontawesome-free-solid/faPlay';
import faChevronLeft from '@fortawesome/fontawesome-free-solid/faChevronLeft';
import faChevronRight from '@fortawesome/fontawesome-free-solid/faChevronRight';
import faPause from '@fortawesome/fontawesome-free-solid/faPause';
import faWrench from '@fortawesome/fontawesome-free-solid/faWrench';
import { classes, extension, handleError } from '/common/util';
import { classes, extension } from '/common/util';
import { TracerApi } from '/apis';
import { actions } from '/reducers';
import { Button, ProgressBar } from '/components';
import { BaseComponent, Button, ProgressBar } from '/components';
import styles from './stylesheet.scss';
@connect(({ player }) => ({ player }), actions)
class Player extends React.Component {
class Player extends BaseComponent {
constructor(props) {
super(props);
......@@ -24,6 +24,8 @@ class Player extends React.Component {
building: false,
};
this.tracerApiSource = null;
this.reset();
}
......@@ -65,17 +67,31 @@ class Player extends React.Component {
}
build(file) {
this.reset();
if (!file) return;
if (this.tracerApiSource) this.tracerApiSource.cancel();
this.tracerApiSource = axios.CancelToken.source();
this.setState({ building: true });
this.reset();
const ext = extension(file.name);
(ext in TracerApi ?
TracerApi[ext]({ code: file.content }) :
Promise.reject(new Error('Language Not Supported')))
.then(traces => this.reset(traces))
.then(() => this.next())
.catch(handleError.bind(this))
.finally(() => this.setState({ building: false }));
if (ext in TracerApi) {
TracerApi[ext]({ code: file.content }, undefined, this.tracerApiSource.token)
.then(traces => {
this.tracerApiSource = null;
this.setState({ building: false });
this.reset(traces);
this.next();
})
.catch(error => {
if (axios.isCancel(error)) return;
this.tracerApiSource = null;
this.setState({ building: false });
this.handleError(error);
});
} else {
this.handleError(new Error('Language Not Supported'));
}
}
isValidCursor(cursor) {
......
import React from 'react';
import { connect } from 'react-redux';
import { classes, handleError } from '/common/util';
import { ResizableContainer } from '/components';
import { classes } from '/common/util';
import { BaseComponent, ResizableContainer } from '/components';
import { actions } from '/reducers';
import styles from './stylesheet.scss';
import { Array1DData, Array2DData, ChartData, Data, GraphData, LogData, MarkdownData } from '/core/datas';
@connect(({ player }) => ({ player }), actions)
class VisualizationViewer extends React.Component {
class VisualizationViewer extends BaseComponent {
constructor(props) {
super(props);
......@@ -82,7 +82,7 @@ class VisualizationViewer extends React.Component {
data[method](...args);
}
} catch (error) {
handleError.bind(this)(error);
this.handleError(error);
}
}
......
export { default as App } from './App';
export { default as BaseComponent } from './BaseComponent';
export { default as Button } from './Button';
export { default as CodeEditor } from './CodeEditor';
export { default as Divider } from './Divider';
......
# Algorithm Visualizer
> Algorithm Visualizer is an interactive online platform that visualizes algorithms from code.
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square)](https://gitter.im/algorithm-visualizer)
[![GitHub contributors](https://img.shields.io/github/contributors/algorithm-visualizer/algorithm-visualizer.svg?style=flat-square)](https://github.com/algorithm-visualizer/algorithm-visualizer/graphs/contributors)
[![GitHub](https://img.shields.io/github/license/algorithm-visualizer/algorithm-visualizer.svg?style=flat-square)](https://github.com/algorithm-visualizer/algorithm-visualizer/blob/master/LICENSE)
Learning algorithms from text and static images is quite boring. For that, there have been many great websites that view animations of various algorithms. However, for us being coders, nothing can be more comprehensible than visualizing the actual working code. So here we introduce Algorithm Visualizer.
[![Screenshot](https://raw.githubusercontent.com/algorithm-visualizer/algorithm-visualizer/master/branding/screenshot.png)](https://algorithm-visualizer.org/)
## Contributing
The project [algorithm-visualizer](https://github.com/algorithm-visualizer) is composed of the following 3 repositories.
* [algorithm-visualizer/algorithms](https://github.com/algorithm-visualizer/algorithms): contains public algorithms shown on the sidebar. [Contribute](https://github.com/algorithm-visualizer/algorithms/blob/master/CONTRIBUTING.md)
* [algorithm-visualizer/tracers](https://github.com/algorithm-visualizer/tracers): contains visualization libraries written in each supported language. [Contribute](https://github.com/algorithm-visualizer/tracers/blob/master/CONTRIBUTING.md)
* [algorithm-visualizer/algorithm-visualizer](https://github.com/algorithm-visualizer/algorithm-visualizer): contains the front-end written in React.js and the back-end written in Node.js. [Contribute](https://github.com/algorithm-visualizer/algorithm-visualizer/blob/master/CONTRIBUTING.md)
Take a moment to read `CONTRIBUTING.md` in the repository you want to contribute to.
../../../../README.md
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册