custom-throttle
delayで処理と処理との間に入れるintervalを指定する
0の場合は、前の処理が終了し次第すぐに実行する
実行結果は返り値のresult propertyに格納されている
実行が飛ばされた場合はexecutedがfalseになる
2021-05-21
21:31:20 queueの処理を実行中に別の処理がqueueに入ってきた時、その処理を消してしまうバグが有ったのを直した
21:07:40 すぐには処理しないようにするoptionを追加した
{immediate: false}を渡す
code: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を削除してから中身を実行する
code: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()とかを連打して試す
/icons/TODO.iconDeno testに置き換える
code:js
import('/api/code/programming-notes/custom-throttle/test.js');
code: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);
code: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')
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)
assert.equal(count, 49)
assert.equal(result2, 'done49')
assert.equal(result3, 'done49')
assert.equal(result4, 'done49')
})
})
})
Deno.icon