delay 带取消功能的延迟函数

160 阅读3分钟

本文参加了由公众号@若川视野发起的每周源码共读活动,  点击了解详情一起参与。

本文已参与「新人创作礼」活动,一起开启掘金创作之路

前言

🤔加油读源码!!!!!

知识点

  • Math.floor()
    返回小于或等于一个给定数字的最大整数。
  • Math.random()
    返回0-1之间的一个浮点数,这个浮点数小于1
  • AbortController
    AbortController接口表示一个对象,允许您根据需要中止一个或多个 Web 请求,文档地址:developer.mozilla.org/zh-CN/docs/…
  • addEventListener()
    注册监听事件
    type(即事件类型,常见的如click, DomContentLoaded, error等)

listener listener既可以是function,也可以是一个带有handleEvent方法的object对象

options

capture(指定事件行为是否为捕获事件,默认为false,即冒泡)

once(指定事件是否只执行一次,默认为false,当为true时,只执行一次后,事件会自动被移除)

passive 默认为false,当指定为true时,如果事件本身执行了preventDefault方法,用户代理将不会做任何事情,取而代之的是生成一个控制台warning

  • removeEventListener()
    移除监听事件

源码

1.简单的延迟执行方法

const delay1 = (ms) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
}
await delay1(1000);
console.log('输出这句'); //一秒后打印这一句

2.可以传参数的延迟函数

const delay2 = (ms, { value } = {}) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(value);
        }, ms);
    });
}
const result = await delay2(1000, { value: '哈哈哈' });
console.log('输出结果', result); //一秒后打印 哈哈哈

3.可以控制延迟是否执行成功或者失败

增加参数willResolve 为true则执行成功 false则会执行reject

const delay3 = (ms, { value, willResolve } = {}) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (willResolve) {
                resolve(value);
            }
            else {
                reject(value);
            }
        }, ms);
    });
}
  try {
        const result = await delay3(1000, { value: '哈哈', willResolve: false });
        console.log('永远不会输出这句');
      }
   catch (err) {
      console.log('输出结果', err); //打印 哈哈
   }

4.增加随机时间返回结果方法

增加了randomInteger随机时间计算函数,该函数返回一个数字,这个就是多少秒后执行

const randomInteger = (minimum, maximum) => {
    // console.log((Math.random() * (maximum - minimum + 1)) + minimum)
    return Math.floor((Math.random() * (maximum - minimum + 1)) + minimum)
};

const createDelay = ({ willResolve }) => (ms, { value } = {}) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (willResolve) {
                resolve(value);
            }
            else {
                reject(value);
            }
            console.log(ms, 'ms')
        }, ms);
    });
}

const createWithTimers = () => {
    const delay = createDelay({ willResolve: true }); //返回成功的延迟函数

    delay.reject = createDelay({ willResolve: false }); //返回失败的延迟函数
    delay.range = (minimum, maximum, options) => delay(randomInteger(minimum, maximum), options); //随机时间返回成功
    console.log(delay, 'delay')
    return delay;
}
const delay4 = createWithTimers();

5.清楚延时,提前执行,在返回的promise实例中增加clear函数 此函数负责清楚延时器 再去

const randomInteger = (minimum, maximum) => Math.floor((Math.random() * (maximum - minimum + 1)) + minimum);

const createDelay = ({ willResolve }) => (ms, { value } = {}) => {
    let timeoutId;
    let settle;
    const delayPromise = new Promise((resolve, reject) => {
        settle = () => {
            if (willResolve) {
                resolve(value);

            }
            else {
                reject(value);
            }
        }
        timeoutId = setTimeout(settle, ms);
    });
    //在promise返回的实例中
    delayPromise.clear = () => {
        clearTimeout(timeoutId);
        timeoutId = null;
        settle(); //此处为啥要在去调用一个settle的原因是,如果不调用 Promise的状态会一直为pedding状态
    };
    console.log(delayPromise, 'delayPromise')
    return delayPromise;
}

const createWithTimers = () => {
    const delay = createDelay({ willResolve: true });
    delay.reject = createDelay({ willResolve: false });
    delay.range = (minimum, maximum, options) => delay(randomInteger(minimum, maximum), options);
    return delay;
}
const delay5 = createWithTimers();
  const delayedPromise = delay5(1000, { value: '哈哈哈' });

            setTimeout(() => {
                delayedPromise.clear();
            }, 300);

            // 300 milliseconds later
            console.log(await delayedPromise);
            //=> '哈哈哈'

6.取消执行延迟函数

使用了AbortController插件

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 = ({ willResolve }) => (ms, { value, signal } = {}) => {
	//判断是否传入AbortController 实例返回对象的signal 如果aborted中止状态为true则返回失败提示信息
	if (signal && signal.aborted) {
		return Promise.reject(createAbortError());
	}

	let timeoutId;
	let settle;
	let rejectFn;
	//创建中止监听的的方法
	const signalListener = () => {
		clearTimeout(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 = setTimeout(settle, ms);
	});
	//判断是否又signal 是则添加监听事件abort
	if (signal) {
		signal.addEventListener('abort', signalListener, { once: true });
	}

	delayPromise.clear = () => {
		clearTimeout(timeoutId);
		timeoutId = null;
		settle();
	};

	return delayPromise;
}

const createWithTimers = () => {
	const delay = createDelay({ willResolve: true });
	delay.reject = createDelay({ willResolve: false });
	delay.range = (minimum, maximum, options) => delay(randomInteger(minimum, maximum), options);
	return delay;
}
const delay6 = createWithTimers();
   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'
            }

End

这期源码让我知道了AbortController插件齐基本就是利用addEventListener添加监听事件然后触发事件执行addEventListener中的函数,清除延时,返回错误信息。