- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第18期,链接:www.yuque.com/ruochuan12/…
使用:
(async() => {
await delay1(1000);
console.log('输出这句');
})();
思路:
怎么延迟?定时器
怎么阻止后续语句的执行?await 后返回promise,控制promise的执行进而控制程序的执行
实现:Promise 和 setTimeout 结合
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;