cancel previous opening explorer stream when opening a new one
parent
4d51f36805
commit
c9fa4ea868
|
@ -17,6 +17,7 @@ import {
|
|||
SimpleTablebaseHit,
|
||||
ExplorerOpts,
|
||||
} from './interfaces';
|
||||
import { CancellableStream } from 'common/ndjson';
|
||||
|
||||
function pieceCount(fen: Fen) {
|
||||
const parts = fen.split(/\s/);
|
||||
|
@ -48,7 +49,8 @@ export default function (root: AnalyseCtrl, opts: ExplorerOpts, allow: boolean):
|
|||
failing = prop<Error | null>(null),
|
||||
hovering = prop<Hovering | null>(null),
|
||||
movesAway = prop(0),
|
||||
gameMenu = prop<string | null>(null);
|
||||
gameMenu = prop<string | null>(null),
|
||||
lastStream = prop<Promise<CancellableStream> | null>(null);
|
||||
|
||||
const checkHash = (e?: HashChangeEvent) => {
|
||||
if ((location.hash === '#explorer' || location.hash === '#opening') && !root.embed) {
|
||||
|
@ -85,27 +87,31 @@ export default function (root: AnalyseCtrl, opts: ExplorerOpts, allow: boolean):
|
|||
failing(err);
|
||||
root.redraw();
|
||||
};
|
||||
const prev = lastStream();
|
||||
if (prev) prev.then(stream => stream.cancel());
|
||||
if (withGames && tablebaseRelevant(effectiveVariant, fen))
|
||||
xhr.tablebase(opts.tablebaseEndpoint, effectiveVariant, fen).then(processData, onError);
|
||||
else
|
||||
xhr.opening(
|
||||
{
|
||||
endpoint: opts.endpoint,
|
||||
endpoint3: opts.endpoint3,
|
||||
db: config.data.db.selected() as ExplorerDb,
|
||||
personal: {
|
||||
player: config.data.playerName.value(),
|
||||
color: root.getOrientation(),
|
||||
lastStream(
|
||||
xhr.opening(
|
||||
{
|
||||
endpoint: opts.endpoint,
|
||||
endpoint3: opts.endpoint3,
|
||||
db: config.data.db.selected() as ExplorerDb,
|
||||
personal: {
|
||||
player: config.data.playerName.value(),
|
||||
color: root.getOrientation(),
|
||||
},
|
||||
variant: effectiveVariant,
|
||||
rootFen: root.nodeList[0].fen,
|
||||
play: root.nodeList.slice(1).map(s => s.uci!),
|
||||
fen,
|
||||
speeds: config.data.speed.selected(),
|
||||
ratings: config.data.rating.selected(),
|
||||
withGames,
|
||||
},
|
||||
variant: effectiveVariant,
|
||||
rootFen: root.nodeList[0].fen,
|
||||
play: root.nodeList.slice(1).map(s => s.uci!),
|
||||
fen,
|
||||
speeds: config.data.speed.selected(),
|
||||
ratings: config.data.rating.selected(),
|
||||
withGames,
|
||||
},
|
||||
processData
|
||||
processData
|
||||
)
|
||||
);
|
||||
},
|
||||
250,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ExplorerData, ExplorerDb, ExplorerSpeed, OpeningData, TablebaseData } from './interfaces';
|
||||
import * as xhr from 'common/xhr';
|
||||
import { readNdJson } from 'common/ndjson';
|
||||
import { readNdJson, CancellableStream } from 'common/ndjson';
|
||||
|
||||
interface OpeningXhrOpts {
|
||||
endpoint: string;
|
||||
|
@ -19,7 +19,7 @@ interface OpeningXhrOpts {
|
|||
withGames?: boolean;
|
||||
}
|
||||
|
||||
export function opening(opts: OpeningXhrOpts, processData: (data: ExplorerData) => void): Promise<void> {
|
||||
export function opening(opts: OpeningXhrOpts, processData: (data: ExplorerData) => void): Promise<CancellableStream> {
|
||||
const endpoint = opts.db == 'player' ? opts.endpoint3 : opts.endpoint;
|
||||
const url = new URL(opts.db === 'lichess' ? '/lichess' : opts.db == 'player' ? '/personal' : '/master', endpoint);
|
||||
const params = url.searchParams;
|
||||
|
|
|
@ -1,31 +1,41 @@
|
|||
export type ProcessLine = (line: any) => void;
|
||||
|
||||
export interface CancellableStream {
|
||||
cancel(): void;
|
||||
end: Promise<void>;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to read a ND-JSON HTTP stream.
|
||||
* `processLine` is a function taking a JSON object. It will be called with each element of the stream.
|
||||
* `response` is the result of a `fetch` request.
|
||||
* https://gist.github.com/ornicar/a097406810939cf7be1df8ea30e94f3e
|
||||
*/
|
||||
export const readNdJson = (processLine: ProcessLine) => (response: Response) => {
|
||||
const stream = response.body!.getReader();
|
||||
const matcher = /\r?\n/;
|
||||
const decoder = new TextDecoder();
|
||||
let buf = '';
|
||||
export const readNdJson =
|
||||
(processLine: ProcessLine) =>
|
||||
(response: Response): CancellableStream => {
|
||||
const stream = response.body!.getReader();
|
||||
const matcher = /\r?\n/;
|
||||
const decoder = new TextDecoder();
|
||||
let buf = '';
|
||||
|
||||
const loop = (): Promise<void> =>
|
||||
stream.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
if (buf.length > 0) processLine(JSON.parse(buf));
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
buf += chunk;
|
||||
const parts = buf.split(matcher);
|
||||
buf = parts.pop() || '';
|
||||
for (const i of parts.filter(p => p)) processLine(JSON.parse(i));
|
||||
return loop();
|
||||
}
|
||||
});
|
||||
const loop = (): Promise<void> =>
|
||||
stream.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
if (buf.length > 0) processLine(JSON.parse(buf));
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
buf += chunk;
|
||||
const parts = buf.split(matcher);
|
||||
buf = parts.pop() || '';
|
||||
for (const i of parts.filter(p => p)) processLine(JSON.parse(i));
|
||||
return loop();
|
||||
}
|
||||
});
|
||||
|
||||
return loop();
|
||||
};
|
||||
return {
|
||||
cancel: () => stream.cancel(),
|
||||
end: loop(),
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue