如何确保start函数仅调用一次?仅修改foo实现兜底触发
解决方案
要实现start仅被调用一次,且bar触发时优先使用bar的调用、foo作为兜底的需求,我们可以通过在foo函数中包装全局start函数并设置延迟兜底定时器的方式实现,仅需修改foo函数,无需改动case1、case2和bar。
修改后的foo函数
function foo() { console.log('foo'); let hasStarted = false; const originalStart = start; let fallbackTimer; // 包装全局start函数,确保仅执行一次 window.start = function(origin) { if (!hasStarted) { hasStarted = true; clearTimeout(fallbackTimer); // 触发后立即清除兜底定时器 originalStart(origin); window.start = originalStart; // 恢复原始start函数,避免影响后续操作 } }; // 设置兜底调用:延迟时间略长于bar的最大可能等待时间(bar最多等待3000ms) fallbackTimer = setTimeout(() => { window.start('foo'); }, 3100); }
实现逻辑说明
- 状态标记与函数包装:在
foo中创建hasStarted标记,同时将全局start替换为包装函数,确保只有第一次调用start会生效。 - 兜底定时器:设置一个延迟3100ms的定时器(比
bar的最大等待时间3000ms稍长),如果bar未触发start,定时器到期后会调用start('foo')作为兜底。 - 触发后清理:一旦
bar触发start('bar'),包装函数会立即清除兜底定时器,并恢复原始start函数,避免影响后续操作。
完整代码验证
const wait = ms => new Promise(res => setTimeout(res, ms)); function foo() { console.log('foo'); let hasStarted = false; const originalStart = start; let fallbackTimer; // 包装全局start函数,确保仅执行一次 window.start = function(origin) { if (!hasStarted) { hasStarted = true; clearTimeout(fallbackTimer); // 触发后立即清除兜底定时器 originalStart(origin); window.start = originalStart; // 恢复原始start函数,避免影响后续操作 } }; // 设置兜底调用:延迟时间略长于bar的最大可能等待时间(bar最多等待3000ms) fallbackTimer = setTimeout(() => { window.start('foo'); }, 3100); } function bar() { console.log('bar'); // 模拟API调用耗时 wait(3000 * Math.random()).then(() => { // 随机决定是否触发start if(Math.random() > 0.5) { start('bar'); } }); } function start(origin) { console.log('start', origin); } // ------ 模拟用户触发场景 ------- function case1() { foo(); bar(); } function case2() { foo(); }
<button onclick="case1()">Case 1</button> <button onclick="case2()">Case 2</button>
符合预期结果
- Case 1(bar触发start):输出顺序为
foo→bar→start bar - Case 1(bar未触发start):输出顺序为
foo→bar→start foo(延迟约3.1秒后输出) - Case 2:输出顺序为
foo→start foo(延迟约3.1秒后输出)
内容的提问来源于stack exchange,提问作者Hao Wu
相关产品推荐
相关产品推荐

