【若川视野 x 源码共读】第18期 带取消功能的延迟函数

162 阅读3分钟

使用:

(async() => {
    await delay1(1000);
    console.log('输出这句');
})();

思路:

怎么延迟?定时器

怎么阻止后续语句的执行?await 后返回promise,控制promise的执行进而控制程序的执行

实现:PromisesetTimeout 结合

const delay1 = (ms) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
}

参考若川大佬的文章,在上面的例子上我们再增加些功能:

  • 传递 value 参数作为结果:利用promise的resolve可传值

  • willResolve 参数决定成功还是失败:调用 resolve还是reject

  • 一定时间范围内随机获得结果:提供randomInteger方法

// 随机生成一个整数时间
const randomInteger = (minimum, maximum) => Math.floor((Math.random() * (maximum - minimum + 1)) + minimum);

// createDelay是一个箭头函数接受参数willResolve,返回一个函数
// 这个被返回的函数接受ms毫秒值和一个对象,对象value属性时要resolve的值 (这叫高阶函数)
const createDelay = ({willResolve}) => (ms, {value} = {}) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(willResolve){
                resolve(value);
            }
            else{
                reject(value);
            }
        }, ms);
    });
}

const createWithTimers = () => {
    // delay是一个函数,在js中除了基本类型一切皆对象
    const delay = createDelay({willResolve: true});
    // 定义delay的reject方法,willResolve固定传值false
    delay.reject = createDelay({willResolve: false});
    // 定义range方法,此函数接受三个参数,前两个参数minimum, maximum传给randomInteger用于生成一个整型时间,
  	// 第三个参数是配置对象,包含value属性
    delay.range = (minimum, maximum, options) => delay(randomInteger(minimum, maximum), options);
    return delay;
}
const delay4 = createWithTimers();
  • 提前清除:提供clear()方法,在此方法中清除定时器并改变promise状态

  • 取消功能:接收一个signal实例,监听其abort方法,在abort方法被调用时清空定时器,定义一个错误对象,并reject这个错误对象.

补充: AbortContoler实例上会有一个signal属性,当AbortContoler实例调用abort方法时,与之关联的 signal 的状态会立刻从起始状态(非终止状态)转变为终止状态,signal的abort事件会被触发;

使用:

(async () => {
    const abortController = new AbortController();
​
    setTimeout(() => {
        abortController.abort();
    }, 500);
​
    try {
        await delay6(1000, {signal: abortController.signal});
    } catch (error) {
        // 500 milliseconds later
        console.log(error.name)
        //=> 'AbortError'
    }
})();

实现细节:

// 1.若状态已变化,说明已取消请求,直接reject  
if (signal && signal.aborted) {
    return Promise.reject(createAbortError());
  }
// 声明取消函数回调
    const signalListener = () => {
        clearTimeout(timeoutId);
        rejectFn(createAbortError());
    }
     
    const cleanup = () => {
    if (signal) {
      signal.removeEventListener('abort', signalListener);
    }
  };
//3.监听abort方法
    if (signal) {
    signal.addEventListener('abort', signalListener, {once: true});
  }
  • 自定义 clearTimeout 和 setTimeout 函数

使用:

const customDelay = delay7.createWithTimers({clearTimeout, setTimeout});
​
(async() => {
    const result = await customDelay(100, {value: '我是若川'});
​
    // Executed after 100 milliseconds
    console.log(result);
    //=> '我是若川'
})();

完整代码:

const randomInteger = (minimum, maximum) => Math.floor((Math.random() * (maximum - minimum + 1)) + minimum);
​
const createAbortError = () => {
  const error = new Error('Delay aborted');
  error.name = 'AbortError';
  return error;
};
​
const createDelay = ({clearTimeout: defaultClear, setTimeout: set, willResolve}) => (ms, {value, signal} = {}) => {
    if (signal && signal.aborted) {
    return Promise.reject(createAbortError());
  }
​
    let timeoutId;
    let settle;
    let rejectFn;
    const clear = defaultClear || clearTimeout;
​
    const signalListener = () => {
        clear(timeoutId);
        rejectFn(createAbortError());
    }
    const cleanup = () => {
    if (signal) {
      signal.removeEventListener('abort', signalListener);
    }
  };
    const delayPromise = new Promise((resolve, reject) => {
        settle = () => {
      cleanup();
      if (willResolve) {
        resolve(value);
      } else {
        reject(value);
      }
    };
​
        rejectFn = reject;
        timeoutId = (set || setTimeout)(settle, ms);
    });
    
    if (signal) {
    signal.addEventListener('abort', signalListener, {once: true});
  }
​
    delayPromise.clear = () => {
    clear(timeoutId);
    timeoutId = null;
    settle();
  };
​
    return delayPromise;
}
​
const createWithTimers = clearAndSet => {
    const delay = createDelay({...clearAndSet, willResolve: true});
    delay.reject = createDelay({...clearAndSet, willResolve: false});
    delay.range = (minimum, maximum, options) => delay(randomInteger(minimum, maximum), options);
    return delay;
}
const delay7 = createWithTimers();
delay7.createWithTimers = createWithTimers;

参考资料: juejin.cn/post/704246… github.com/sindresorhu…