Scrapboxからマーカーのデータを取得する
マーカーやコメントのデータは、Scrapboxのページに記録される。
Scrapboxプロジェクトの全ページデータを取得してからマーカーを表示すると、以下のような問題が生じる。
サーバーへの負担
Scrapboxサーバーに対して、ページ数分(例えば数百 ~ 数千)のHTTPリクエストが発生する。
リアルタイム性の欠如
マーカーを引いても、新しいマーカーをブラウザに表示させるためには、再び全ページを取得する必要がある。
マーカーをScrapboxに記録するとき、Scrapboxページには
[annos://example.com/]
のようなリンクが置かれる。この
annos://
から始まるリンクによって、Scrapboxプロジェクトから効率的にデータを取得している。例えばブラウザが https://scrapbox.io/hata6502 にアクセスしたとき、annoは
[annos://scrapbox.io/hata6502]
リンクを持つページだけのデータを取得している。Scrapboxサーバーへの負担を考慮し、HTTPリクエストを1秒あたり1回までに制限している。
p-queueは、Promiseのキューを実装したnpm packageである。
p-queueを利用して、帯域制限付きのFetch APIを実装した。
ts
import PQueue from "p-queue";
const fetchQueue = new PQueue({ interval: 5000, intervalCap: 5 });
const queuedFetch = (input: RequestInfo | URL, init?: RequestInit) =>
fetchQueue.add(() => fetch(input, init), { throwOnTimeout: true });
// 使用例
const response = await queuedFetch("https://scrapbox.io/api/pages/hata6502/anno");
さらに、素早くページを移動しても、Scrapboxとの通信キューが滞留しないようにしている。
ページを移動したとき、Web APIであるAbortControllerを使って、Scrapboxとの通信をキャンセルしている。
以下の疑似コードのように、AbortSignalを監視すれば処理をキャンセルできる。
ts
const functionWithFetch = async(signal) => {
// 省略
// fetchする前に、処理のキャンセルを要求されていないか確認する。
if (signal.aborted) {
throw new DOMException("Aborted", "AbortError");
}
const response = await fetch(
// 省略
};
const abortController = new AbortController();
await functionWithFetch(abortController.signal);
// 処理をキャンセルするとき
abortController.abort();