Skip to content
On this page

delay

delayPromise 延迟一段时间且带有取消功能的轻量级 JS 库。

实现原理

通过 setTimeout 来延迟执行改变 Promise 的状态。 AbortController 来实现取消的功能。

js
const abortController = new AbortController();

const delay = (timeout) => {
  let timeoutId = null
  let rejectFn = null

  abortController.signal.addEventListener('abort', () => {
    clearTimeout(timeoutId)
    rejectFn()
  }, {
    once: true
  })

  return new Promise((resolve, reject) => {
    rejectFn = reject
    timeoutId = setTimeout(() => {
      resolve()
    }, timeout)
  })
}

setTimeout(() => {
  abortController.about()
})

使用

js
const delay = require('delay');

(async () => {
	bar();
	await delay(100);
	// Executed 100 milliseconds later
	baz();
})();

// 传递一个值
(async() => {
	const result = await delay(100, {value: '🦄'});
	// Executed after 100 milliseconds
	console.log(result);
	//=> '🦄'
})();

// 一个失败的延迟 Promise
(async () => {
	try {
		await delay.reject(100, {value: new Error('🦄')});

		console.log('This is never executed');
	} catch (error) {
		// 100 milliseconds later
		console.log(error);
		//=> [Error: 🦄]
	}
})();

// 提前结束延迟
(async () => {
	const delayedPromise = delay(1000, {value: 'Done'});

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

	// 500 milliseconds later
	console.log(await delayedPromise);
	//=> 'Done'
})();

// AbortSignal 中止
(async () => {
	const abortController = new AbortController();

	setTimeout(() => {
		abortController.abort();
	}, 500);

	try {
		await delay(1000, {signal: abortController.signal});
	} catch (error) {
		// 500 milliseconds later
		console.log(error.name)
		//=> 'AbortError'
	}
})();

// 自定义延迟函数
// 可以自定义 clearTimeout 和 setTimeout
const customDelay = delay.createWithTimers({clearTimeout, setTimeout});

(async() => {
	const result = await customDelay(100, {value: '🦄'});

	// Executed after 100 milliseconds
	console.log(result);
	//=> '🦄'
})();

源码解析

randomInteger 函数

js
// 获取最小值到最大值区间的随机整数
const randomInteger = (minimum, maximum) => Math.floor((Math.random() * (maximum - minimum + 1)) + minimum);

createAbortError 函数

js
// 取消延迟函数的错误信息创建函数
const createAbortError = () => {
	const error = new Error('Delay aborted');
	error.name = 'AbortError';
	return error;
};

createWithTimers 函数

js
// 创建一个新的 delay 实例
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;
};

createDelay 核心函数

js
/**
 * defaultClear:自定义的一个清除函数
 * set:自定义的倒计时函数
 * willResolve:成功还是失败的 Promise
 * 
 * ms:倒计时毫秒数
 * value:成功和失败的结果
 * signal:AbortController.signal
 */
const createDelay = ({clearTimeout: defaultClear, setTimeout: set, willResolve}) => (ms, {value, signal} = {}) => {
	if (signal && signal.aborted) {
    // signal 已经是中止状态
		return Promise.reject(createAbortError());
	}

	let timeoutId; // 延迟函数Id
	let settle; // 延迟函数的回调
	let rejectFn; // Promise 的 reject 函数
	const clear = defaultClear || clearTimeout; // 延迟函数的清空函数

  // AbortController.signal 的 abort 监听函数,调用 abortController.abort() 时触发
	const signalListener = () => {
		clear(timeoutId);
		rejectFn(createAbortError());
	};

  // 移除 abort 的监听函数
	const cleanup = () => {
		if (signal) {
			signal.removeEventListener('abort', signalListener);
		}
	};

  // 延迟执行的 Promise 函数
	const delayPromise = new Promise((resolve, reject) => {
		settle = () => {
			cleanup();
			if (willResolve) {
				resolve(value);
			} else {
				reject(value);
			}
		};

		rejectFn = reject;
		timeoutId = (set || setTimeout)(settle, ms);
	});

	if (signal) {
    // 监听 abort 函数
		signal.addEventListener('abort', signalListener, {once: true});
	}

  // 调用可提前结束延迟函数
	delayPromise.clear = () => {
		clear(timeoutId);
		timeoutId = null;
		settle();
	};

	return delayPromise;
};