面试官:请手写一个带取消功能的延迟函数,axios 取消功能的原理是什么

大家好,我是若川。最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。

本文仓库 https://github.com/lxchuan12/delay-analysis.git,求个star^_^[1]

源码共读活动 每周一期,已进行到17期。于是搜寻各种值得我们学习,且代码行数不多的源码。delay 主文件仅70多行[2],非常值得我们学习。

阅读本文,你将学到:

1. 学会如何实现一个比较完善的 delay 函数
2. 学会使用 AbortController 实现取消功能
3. 学会面试常考 axios 取消功能实现
4. 等等

2. 环境准备

# 推荐克隆我的项目,保证与文章同步
git clone https://github.com/lxchuan12/delay-analysis.git
# npm i -g yarn
cd delay-analysis/delay && yarn i
# VSCode 直接打开当前项目
# code .
# 我写的例子都在 examples 这个文件夹中,可以启动服务本地查看调试
# 在 delay-analysis 目录下
npx http-server examples
# 打开 http://localhost:8080# 或者克隆官方项目
git clone https://github.com/sindresorhus/delay.git
# npm i -g yarn
cd delay && yarn i
# VSCode 直接打开当前项目
# code .

3. delay

我们从零开始来实现一个比较完善的 delay 函数[3]

3.1 第一版 简版延迟

要完成这样一个延迟函数。

3.1.1 使用

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

3.1.2 实现

PromisesetTimeout 结合实现,我们都很容易实现以下代码。

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

我们要传递结果。

3.2 第二版 传递 value 参数作为结果

3.2.1 使用

(async() => {const result = await delay2(1000, { value: '我是若川' });console.log('输出结果', result);
})();

我们也很容易实现如下代码。传递 value 最后作为结果返回。

3.2.2 实现

因此我们实现也很容易实现如下第二版。

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

这样写,Promise 永远是成功。我们也需要失败。这时我们定义个参数 willResolve 来定义。

3.3 第三版 willResolve 参数决定成功还是失败。

3.3.1 使用

(async() => {try{const result = await delay3(1000, { value: '我是若川', willResolve: false });console.log('永远不会输出这句');}catch(err){console.log('输出结果', err);}
})();

3.3.2 实现

加个 willResolve 参数决定成功还是失败。于是我们有了如下实现。

const delay3 = (ms, {value, willResolve} = {}) => {return new Promise((resolve, reject) => {setTimeout(() => {if(willResolve){resolve(value);}else{reject(value);}}, ms);});
}

3.4 第四版 一定时间范围内随机获得结果

延时器的毫秒数是写死的。我们希望能够在一定时间范围内随机获取到结果。

3.4.1 使用

(async() => {try{const result = await delay4.reject(1000, { value: '我是若川', willResolve: false });console.log('永远不会输出这句');}catch(err){console.log('输出结果', err);}const result2 = await delay4.range(10, 20000, { value: '我是若川,range' });console.log('输出结果', result2);
})();

3.4.2 实现

我们把成功 delay 和失败 reject 封装成一个函数,随机 range 单独封装成一个函数。

const randomInteger = (minimum, maximum) => Math.floor((Math.random() * (maximum - minimum + 1)) + minimum);const createDelay = ({willResolve}) => (ms, {value} = {}) => {return new Promise((relove, reject) => {setTimeout(() => {if(willResolve){relove(value);}else{reject(value);}}, ms);});
}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 delay4 = createWithTimers();

实现到这里,相对比较完善了。但我们可能有需要提前结束。

3.5 第五版 提前清除

3.5.1 使用

(async () => {const delayedPromise = delay5(1000, {value: '我是若川'});setTimeout(() => {delayedPromise.clear();}, 300);// 300 milliseconds laterconsole.log(await delayedPromise);//=> '我是若川'
})();

3.5.2 实现

声明 settle变量,封装 settle 函数,在调用 delayPromise.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);});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 delay5 = createWithTimers();

3.6 第六版 取消功能

我们查阅资料可以知道有 AbortController 可以实现取消功能。

caniuse AbortController[4]

npm abort-controller[5]

mdn AbortController[6]

fetch-abort[7]

fetch#aborting-requests[8]

yet-another-abortcontroller-polyfill[9]

3.6.1 使用

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

3.6.2 实现

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} = {}) => {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);});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();

3.7 第七版 自定义 clearTimeout 和 setTimeout 函数

3.7.1 使用

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

3.7.2 实现

传递 clearTimeout, setTimeout 两个参数替代上一版本的clearTimeout,setTimeout。于是有了第七版。这也就是delay的最终实现。

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;

4. axios 取消请求

axios取消原理是:通过传递 config 配置 cancelToken 的形式,来取消的。判断有传cancelToken,在 promise 链式调用的 dispatchRequest 抛出错误,在 adapterrequest.abort() 取消请求,使 promise 走向 rejected,被用户捕获取消信息。

更多查看我的 axios 源码文章取消模块 学习 axios 源码整体架构,取消模块(可点击)

5. 总结

我们从零开始实现了一个带取消功能比较完善的延迟函数。也就是 delay 70多行源码[11]的实现。

包含支持随机时间结束、提前清除、取消、自定义 clearTimeout、setTimeout等功能。

取消使用了 mdn AbortController[12] ,由于兼容性不太好,社区也有了相应的 npm abort-controller[13] 实现 polyfill

yet-another-abortcontroller-polyfill[14]

建议克隆项目启动服务调试例子,印象会更加深刻。

# 推荐克隆我的项目,保证与文章同步
git clone https://github.com/lxchuan12/delay-analysis.git
cd delay-analysis
# 我写的例子都在 examples 这个文件夹中,可以启动服务本地查看调试
npx http-server examples
# 打开 http://localhost:8080

最后可以持续关注我@若川。欢迎加我微信 ruochuan12 交流,参与 源码共读 活动,每周大家一起学习200行左右的源码,共同进步。

参考资料

[1]

本文仓库 https://github.com/lxchuan12/delay-analysis.git,求个star^_^: https://github.com/lxchuan12/delay-analysis.git

[2]

delay 主文件仅70多行: https://github.com/sindresorhus/delay/blob/main/index.js



更多点击阅读原文查看。

a1a9a663ef5e46d353c32fbaaadb0dcc.gif

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看年度总结。
同时,最近组织了源码共读活动,帮助3000+前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。

d7595a563eed89e6de347d675d91bc3d.png

识别方二维码加我微信、拉你进源码共读

今日话题

略。分享、收藏、点赞、在看我的文章就是对我最大的支持~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275068.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

facebook 面试_如何为您的Facebook产品设计面试做准备

facebook 面试重点 (Top highlight)Last month, I joined Facebook to work on Instagram DMs and as a way to pay it forward, I 上个月,我加入了Facebook,从事Instagram DM的工作,作为一种支付方式,我 offered to help anyone…

8年了,开始写点东西了

大家好,我是若川。最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。今天分享一位大佬的文章…

荒径 弗罗斯特_弗罗斯特庞克,颠覆性城市建设者

荒径 弗罗斯特Most gamers are familiar with Will Wright’s famous SimCity series. It created the city building genre and there have been many attempts over the years to ape it. But few developers have been bold enough to completely deconstruct the formula; …

Gitee 如何自动部署博客 Pages?推荐用这个GitHub Actions!

大家好,我是若川。最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。前段时间我把自己的博客…

现在流行的画原型图工具_原型资源图:8种流行原型工具的综合指南

现在流行的画原型图工具Although tools are not the most important things to learn as a UX designer, inevitably you need to use it in order to achieve your more important goals, to solve user’s problems. This article covers today’s 8 popular UX prototyping …

持续5个月,200+笔记,3千多人参与,邀请你来学源码~

注意:本文点击文末阅读原文可查看文中所有链接。我正在参加掘金年度人气作者投票活动,大家有空可以加微信群帮忙投票,感谢大家!想起今天还没发文,就开放下微信群二维码,大家扫码进群读源码和帮忙投票吧。群…

第2年,倒数第3天,1.5万票,感动!

1源码共读大家好,我是若川。众所周知。从8月份开始,我组织了源码共读活动,至今已经有5个月了,每周一期,进行到了第18期。每周坚持写源码解读文章,每天坚持答疑解惑,帮助了很多人学会看源码&…

启发式搜索给神经网络_神经科学如何支持UX启发式

启发式搜索给神经网络重点 (Top highlight)Interaction and UX designers have long known and used heuristics to guide the creation of a user-friendly interface. We know empirically that these principles work, and they make “common sense”. These heuristics th…

Django实战(1):需求分析和设计

Depot是《Agile Web Development with Rails》中的一个购物车应用。 该书中用多次迭代的方法,逐步实现购物车应用,使很多人走上了rails开发的道路。 遗憾的是Django世界中好像没有类似的指引,也许是因为pythoner 不需要具体的例子。 但是如果…

海浪 shader_海浪下的发现

海浪 shaderI’ve been playing Subnautica for over 25 hours now, and likely have at least that many more to go. The game puts you in the shoes of a crew member on the Aurora, a spaceship that suffers a catastrophic incident and plummets to the largely ocean…

最后一天,特邀小姐姐配音拉票,今日可投28票

1源码共读大家好,我是若川。最后一天,特邀小姐姐配音拉票,超级好听。众所周知。从8月份开始,我组织了源码共读活动,至今已经有5个月了,每周一期,进行到了第18期。每周坚持写源码解读文章&#x…

对数据可视化的理解_使数据可视化更容易理解

对数据可视化的理解Data is weaving its way into almost all aspects of our lives since the past decade. Our ability to store more information in smaller and smaller spaces has encouraged us to make sure we leave no information out. The ease of collecting inf…

面试官:项目中常用的 .env 文件原理是什么?如何实现?

1. 前言大家好,我是若川。持续组织了5个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。本文仓库 h…

梯度下降法和随机梯度下降法

1. 梯度 在微积分里面,对多元函数的参数求∂偏导数,把求得的各个参数的偏导数以向量的形式写出来,就是梯度。比如函数f(x,y), 分别对x,y求偏导数,求得的梯度向量就是(∂f/∂x, ∂f/∂y)T,简称grad f(x,y)或者▽f(x,y)。对于在点(x…

一张图看程序媛阿源的2021个人年度流水账

大家好,我是若川。持续组织了5个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。本文来自读者阿源小…

案例研究:设计与方法_如何进行1小时的重新设计(案例研究)

案例研究:设计与方法速度设计简介 (Intro to Speed Designing) I’ve been an advocate of speed redesigning technique for a while. The idea is simple — decrease the hand-eye lag and make super quick decisions, seemingly without thinking. The logic behind it is…

图文并茂重新认识下递归

大家好,我是若川。持续组织了5个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。对于大部分前端(包…

unity 全息交互ui_UI向3D投影全息界面的连续发展

unity 全息交互uiThe user interface has been natural in its evolution and strategically heading towards the 3D-projection holographic interface (3D-PHI) era.用户界面在其发展过程中一直很自然,并且在战略上正朝着3D投影全息界面( 3D-PHI )时代迈进。 Si…

前端构建新世代,Esbuild 原来还能这么玩!

大家好,我是若川。持续组织了5个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。今天分享一篇esbui…

平面设计师和ui设计师_平面设计师为什么要享受所有乐趣?

平面设计师和ui设计师Graphic designers are pretty cool. We have to admit that. Be it their dressing style, their attitude and most importantly their enviable gadgets. Large Mac monitor, wacom tablet, drawing sets, swatchbooks , iPad pro with pencil, humungo…