开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
【若川视野 x 源码共读】第18期 | delay 带取消功能的延迟函数 点击了解本期详情一起参与。
今天阅读的是:delay
字面理解,这个库的作用是做延迟执行的
我们先看看测试用例
// index.test-d.ts
// 延迟 200ms
expectType<ClearablePromise<void>>(delay(200));
// 延迟 200ms,支持输入string
expectType<ClearablePromise<string>>(delay(200, { value: "🦄" }));
// 延迟 200ms,支持输入number
expectType<ClearablePromise<number>>(delay(200, { value: 0 }));
// 支持取消执行
expectType<ClearablePromise<void>>(
delay(200, { signal: new AbortController().signal })
);
// 支持随机延迟时间
expectType<ClearablePromise<number>>(delay.range(50, 200, { value: 0 }));
// 支持异常处理
expectType<ClearablePromise<never>>(delay.reject(200, { value: "🦄" }));
expectType<ClearablePromise<never>>(delay.reject(200, { value: 0 }));
// 自定义定时器
const customDelay = delay.createWithTimers({ clearTimeout, setTimeout });
expectType<ClearablePromise<void>>(customDelay(200));
expectType<ClearablePromise<string>>(customDelay(200, { value: "🦄" }));
expectType<ClearablePromise<number>>(customDelay(200, { value: 0 }));
expectType<ClearablePromise<never>>(customDelay.reject(200, { value: "🦄" }));
expectType<ClearablePromise<never>>(customDelay.reject(200, { value: 0 }));
const unrefDelay = delay.createWithTimers({
clearTimeout,
setTimeout(...args) {
return setTimeout(...args).unref();
},
});
这个库是使用Promise + setTimeout实现的
总结一下,具体需要实现的功能点:
- 支持传入延时时间
- 传递
value参数作为结果导出- 可以输入数字
- 可以输入字符串
- 支持取消功能
- 支持随机延时时间
- 能够失败返回
- 支持传入自定义定时器
源码分析
"use strict";
// From https://github.com/sindresorhus/random-int/blob/c37741b56f76b9160b0b63dae4e9c64875128146/index.js#L13-L15
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 } = {}) => {
// 检查是否传入Abort Control
// 这个跟axios中的很类似
// https://axios-http.com/docs/cancellation
// https://developer.mozilla.org/en-US/docs/Web/API/AbortController
if (signal && signal.aborted) {
return Promise.reject(createAbortError());
}
let timeoutId;
let settle;
let rejectFn;
const clear = defaultClear || clearTimeout;
// 取消时 重置并返回结果
const signalListener = () => {
clear(timeoutId);
rejectFn(createAbortError());
};
// 清楚abort事件
const cleanup = () => {
if (signal) {
signal.removeEventListener("abort", signalListener);
}
};
const delayPromise = new Promise((resolve, reject) => {
settle = () => {
cleanup();
// 根据传入willResolve,决定成功返回还是失败返回
if (willResolve) {
resolve(value);
} else {
reject(value);
}
};
rejectFn = reject;
// 判断是否传入自定义setTimeout
// 如果没有传入,则根据传入的 ms 创建
timeoutId = (set || setTimeout)(settle, ms);
});
if (signal) {
// signal.abort() 取消请求
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 delay = createWithTimers();
delay.createWithTimers = createWithTimers;
module.exports = delay;
// TODO: Remove this for the next major release
module.exports.default = delay;
总结
通过对上述源码分析,我们可以得知以下几点:
- 通过在
Promise中设定setTimeout来实现延时执行的功能 - 使用了
Abort Control来实现取消的功能 - 通过
Math.floor()向下取整
相关链接