generated at
custom-throttle
throttleを少し組み替えたやつ
処理を間引くのに使う
async-singletonをベースにしている
delay で処理と処理との間に入れるintervalを指定する
0 の場合は、前の処理が終了し次第すぐに実行する
実行結果は返り値の result propertyに格納されている
実行が飛ばされた場合は executed false になる

2021-05-21
21:31:20 queue の処理を実行中に別の処理が queue に入ってきた時、その処理を消してしまうバグが有ったのを直した
21:07:40 すぐには処理しないようにするoptionを追加した
{immediate: false} を渡す

script.js
export function throttle(callback, delay = 0, {immediate = true} = {}) { if (typeof callback !== 'function') throw new Error('argument is not function.') let queue = undefined; let running = false; const sleep = delay > 0 ? () => new Promise(resolve => setTimeout(() => resolve(), delay)) : () => {}; const runNext = async () => { await sleep(); if (!queue) { running = false; return; }

「queueの中身を実行→queueを削除」だと、queueの中身の非同期関数を実行している間に新しいqueueが入ってきてしまうかもしれないので、先にqueueを削除してから中身を実行する
script.js
const {parameters, resolve} = queue; queue = undefined; resolve({result: await callback(...parameters), executed: true}); await runNext(); }; return (...parameters) => new Promise(async resolve => { if (running) { queue?.resolve?.({executed: false}); queue = {parameters, resolve}; return; } running = true; if (immediate) { // 処理が来たら直ちに実行する resolve({result: await callback(...parameters), executed: true}); } else { // 最初の処理もdelayだけ待つ queue?.resolve?.({executed: false}); queue = {parameters, resolve}; } await runNext(); }); }

test code
開発コンソールで await func1() とかを連打して試す
TODODeno testに置き換える
libraryはtincanを使う
test内容はgithubshokai/async-throttle を参考にする
js
import('/api/code/programming-notes/custom-throttle/test.js');
test.js
import {throttle} from './script.js'; window.func1 = throttle(async () => { const res = await fetch(`/api/pages/${scrapbox.Project.name}`); return await res.json(); }, 1000, {immediate: false}); window.func2 = throttle(async () => { const res = await fetch(`/api/pages/${scrapbox.Project.name}`); return await res.json(); }, 0);

js
/* eslint-env mocha */ const asyncThrottle = require('../') const { assert } = require('chai') const { delay } = require('./delay') describe('async-throttle', function () { describe('one bye one', function () { describe('without arguments', function () { it('acts as normal async-await', async function () { let count = 0 const throttled = asyncThrottle(async function () { count += 1 await delay(10) return `done${count}` }) const result = await throttled() const result2 = await throttled() const result3 = await throttled() assert.equal(count, 3) assert.equal(result, 'done1') assert.equal(result2, 'done2') assert.equal(result3, 'done3') }) }) describe('with arguemnts', function () { it('acts as normal async-await', async function () { let count = 0 const throttled = asyncThrottle(async function (...increments) { if (increments.length > 0) count += increments.reduce((a, b) => a + b) await delay(10) return `done${count}` }) const result = await throttled(1) const result2 = await throttled(2, 3) const result3 = await throttled(4, 5, 6, 7, 8, 9) const result4 = await throttled() assert.equal(count, 45) assert.equal(result, 'done1') assert.equal(result2, 'done6') assert.equal(result3, 'done45') assert.equal(result4, 'done45') }) }) }) describe('trailing: false', function () { it('suppress multiple call', async function () { let count = 0 const throttled = asyncThrottle(async function (...increments) { if (increments.length > 0) count += increments.reduce((a, b) => a + b) await delay(10) return `done${count}` }) // {trailing: false} is default option throttled(1) throttled(2) assert.equal(count, 1) await delay(100) throttled(3, 4, 5) throttled(6) throttled(7) throttled(8) throttled(9, 10, 11) assert.equal(count, 13) await delay(100) const result = await throttled(12, 13, 14) assert.equal(count, 52) assert.equal(result, 'done52') const [result2, result3, result4] = await Promise.all([throttled(15, 16, 17), throttled(18), throttled(19)]) assert.equal(count, 100) assert.equal(result2, 'done100') assert.equal(result3, 'done100') assert.equal(result4, 'done100') }) }) describe('trailing: true', function () { it('suppress multiple call, but call once finally.', async function () { let count = 0 const throttled = asyncThrottle(async function (...increments) { if (increments.length > 0) count += increments.reduce((a, b) => a + b) await delay(100) return `done${count}` }, { trailing: true }) throttled(1) throttled(2) throttled(3) const result = await throttled(4) assert.equal(count, 5) assert.equal(result, 'done5') throttled(5) throttled(6) throttled(7) throttled(8) throttled(9) const [result2, result3, result4] = await Promise.all([throttled(10), throttled(11), throttled(12, 13, 14)]) assert.equal(count, 49) assert.equal(result2, 'done49') assert.equal(result3, 'done49') assert.equal(result4, 'done49') }) }) })

Deno