複数回呼び出されるcallbackをPromisicationするやついろいろ
基本
code:ts
export function getPromiseSettledAnytimes<T, E = unknown>() {
let _resolve: ((value: T) => void) | undefined;
let _reject: ((value: E) => void) | undefined;
const waitForSettled = () => new Promise<T>(
(res, rej) => {
_resolve = res;
_reject = rej;
},
);
const resolve = (value: T) => _resolve?.(value);
const reject = (reason: E) => _reject?.(reason);
}
callbackをprimisifyする
処理中に入ったeventを全部無視する
code:ts
Event,
Error,
();
addEventListener("event", listener);
while (true) {
const event = await listenEvent();
// ループの終了までに発火したeventは全て破棄する
}
発火したすべてのeventを保持する
code:ts
const queue = [] as ({ success: true, value: T } |
{ success: false, value: E })[];
let _resolve: ((value: T) => void) | undefined;
let _reject: ((value: E) => void) | undefined;
const reset = () => _resolve = _reject = undefined;
const waitForSettled = () => new Promise<T>(
(res, rej) => {
if (queue.length > 0) {
const value = queue.shift()!;
if (value.success) {
res(value.value);
} else {
rej(value.value);
}
return;
}
_resolve = res;
_reject = rej;
},
);
const resolve = (value: T) => {
if (_resolve) {
_resolve(value);
reset();
return;
}
queue.push({ success: true, value });
};
const reject = (value: E) =>
if (_reject) {
_reject(value);
reset();
return;
}
queue.push({ success: false, value });
};
addEventListener("event", resolve);
while (true) {
const { value: event } = await waitForSettled();
// ...
// この間に発火したイベントも次以降のループで処理する
}
最後に発火した未処理eventだけ保持する
↑のqueueの最後尾以外を破棄する
Promiseを解決順に返す
Iterable<Promise<T>>→AsyncGenerator<{ success: true; value: T; } | { success: false; value: E; }>
code:ts
type Result<T, E> = { success: true; value: T; } | { success: false; value: E; };
async function* sortSettled<T, E = unknown>(
list: Iterable<Promise<T>>
): AsyncGenerator<Result<T, E>, void, unknown> {
const queue = [] as Result<T, E>[];
let _resolve: ((value: Result<T, E>) => void) | undefined;
/** _resolveがなければqueueに貯め、あれば消費する */
const push = (value: Result<T, E>) => {
if (!_resolve) {
queue.push(value);
return;
}
_resolve(value);
_resolve = undefined;
};
/** queueから一つ取り出す。空なら_resolveをセットする */
const shift = async () => queue.length > 0 ?
queue.shift()! :
await new Promise<Result<T, E>>(
(resolve) => _resolve = resolve
);
let count = 0;
/** Promiseが解決したらqueueにいれるよう仕掛けておく */
for (const item of list) {
count++;
item.then((value) => push({
success: true,
value,
}))
.catch((reason) => push({
success: false,
value: reason as E,
}));
}
/** 終わったものから順次返す */
for (let i = 0; i < count; i++) {
yield await shift();
}
}