JS异步防抖控制逻辑
作者:秋了秋 发表时间:2023年07月27日
在复杂的代码中,很难避免同一个函数或者代码片段只调用一次,防抖的作用是让相同的程序代码在设定时间内只调用一次,以达到“节能减排”的目的。实现上这里涉及到一个时间,所以在程序逻辑是在某个时间段内调用某个代码A,A不会立即执行,比如设置一个1s后执行的定时器,如果1s后其它地方又发起了调用A代码,则取消上次的定时器,重新设定一个1s的定时器,直到1s后没人调用A代码则执行A代码并销毁定时器,标记完成一个任务。
这里就会出现异步逻辑,很难判断A代码有没有执行完,可以通过以下方案解决:
this.doOnce = (function () { //核心异步控制逻辑 let nameMaps = {}, outTimerMap = {}, taskEnterTimeManage = {}, sleepTime = 100; //这个时间之内有相同任务进入则取消上一个任务的执行,创建新任务;如果最后一个任务后超过这个时间没有任务进来表示任务组结束, 初始默认值为0.1秒,如果程序进入间隔时间低于这个值会通过程序学习改变减少等待时间 function a(fn, taskName) { return new Promise(async function (resolve, reject) { if (!taskName) { taskName = 'noName'; } if (nameMaps[taskName]) { clearTimeout(nameMaps[taskName]); delete nameMaps[taskName]; taskEnterTimeManage[taskName].push(Date.now()); } else { nameMaps[taskName] = {}; taskEnterTimeManage[taskName] = [Date.now()]; } if (taskEnterTimeManage[taskName].length >= 2) { let allTime = 0, count = 0; for (let i = 0; i < taskEnterTimeManage[taskName].length; i++) { if (i < 50 && taskEnterTimeManage[taskName][i + 1]) { //最大50个即可推断出平均值,节省性能 allTime += taskEnterTimeManage[taskName][i + 1] - taskEnterTimeManage[taskName][i]; count++; } else { break; } } sleepTime = parseInt(allTime / count + 10); } nameMaps[taskName] = setTimeout(async () => { await fn(); clearTask(); }, sleepTime); function clearTask() { if (outTimerMap[taskName]) { clearTimeout(outTimerMap[taskName]); delete outTimerMap[taskName]; delete nameMaps[taskName]; } outTimerMap[taskName] = setTimeout(() => { delete outTimerMap[taskName]; delete nameMaps[taskName]; delete taskEnterTimeManage[taskName]; resolve(true); //sleepTime时间到了还没有新任务过来则表示该批任务完成 }, sleepTime); } }); } return async function (fn, args, taskName) { await a(async function () { await fn(...args); }, taskName); }; })();
使用方法(测试代码):
//业务代码(测试函数-模拟异步函数) async function delayLogSomthing(somthing, count) { await sleep(10); console.log(somthing, count); } async function sleep(sleepTime) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(true); }, sleepTime) }) } (async function(){ console.log('测试场景一:'); for(let i = 0; i < 10; i++) { await sleep(10); doOnce(delayLogSomthing, ['netblog.cn', i], 'one',); } //await sleep(10); doOnce(delayLogSomthing, ['netblog.cn', 'one'], 'one'); console.log(11111); })(); (async function(){ await sleep(500); console.log('_________________'); console.log('测试场景二:'); for(let i = 0; i < 10; i++) { await doOnce(delayLogSomthing, ['netblog.cn', i], 'two'); } await doOnce(delayLogSomthing, ['netblog.cn', 'two'], 'two'); console.log(22222); })();
doOnce第一个参数为需要执行的函数,第二个参数为第一个执行函数的参数集合,第三个参数为组别名(可以不传),不传的话都会把所有不传的归为一组任务。
1
文章作者: “秋了秋”个人博客,本站鼓励原创。
转载请注明本文地址:http://netblog.cn/blog/502.html