很多人说这个没用,还嘲讽深入理解,没有解决麻烦和任何问题
我举个最简单的例子,python协程的async await,代码如下:
import asyncio async def sleep(seconds): await asyncio.sleep(seconds) async def main(num=0): # 并发执行多个异步任务 print(num) await sleep(0.0001) await main(num + 1) asyncio.run(main())
输出到1000就会堆栈溢出,而用js的promise实现一样的函数,却不会
然而一旦把sleep去掉,就会立马堆栈溢出
function sleep(time){ return new Promise(resolve => setTimeout(resolve, time)); } async function main(num=0){ console.log(num) //去掉sleep会堆栈溢出 await sleep(1); await main(num+1) } main()
因为js中的await实际上等效于then,也就是下面的代码,每次循环都是新的任务提交到事件循环队列,所以不会堆栈累加:
async function main(num=0){ console.log(num) sleep(1).then(()=>{ main(num+1) }) }
导致函数堆栈溢出的因素都可以说成不重要。没有任何意义,到底是自己没有认真学,还是真的不重要,各位自行判断吧
不要在说有什么用,有什么应用场景了,手写apply call bind这些面试题有应用场景吗?
单纯为了兴趣研究下,就跟普通promise输出顺序的面试题一样,也可以写个类似的
async function main() { await { then(func) { console.log(1) setTimeout(func,0); console.log(2) } } console.log(3) } main() ``
![]() | 1 Zhuzhuchenyan 108 天前 ![]() |
2 wefgonujnopu OP @Zhuzhuchenyan nb ,这么快就有人答出来了 |
3 wefgonujnopu OP 对 promise 和 await 理解深入才能答出来。这个感觉可以当面试题,问 ai ai 都说做不到,还得提示下才回复正确答案 |
![]() | 4 yimity 107 天前 yield 也应该可以实现。 |
5 lscho 107 天前 ![]() 感觉 op 刚学 js 。。。。 之前 es6 提案通过,但是未正式实现的时候,都是这样实现的 |
7 taotaodaddy 107 天前 ![]() AI 没问题的,gemini 2.5 pro 明确回答 await 关键字实际上等待的并不仅仅是 Promise 对象,而是任何符合 "Thenable" 规范的对象 它也完美得给出了与 1 楼基本相同的代码 |
8 muzig 107 天前 via Android ![]() 当面试回答的意义就是“深入理解”? |
9 humbass 107 天前 via Android 试了下用以下关键字问 AI (我手机上装了 7 种),只有 gemini, deepseek 回答正确。(_)!! ``` promp js 不使用 promise ,写一个支持 await 调用的 sleep 函数 |
10 dcsuibian 107 天前 ![]() 感觉你不适合当面试官。 这个问题没有解决任何问题,徒增麻烦。 |
11 lscho 107 天前 @Plumbiu 有没有可能我说的是 Promise 对象。。。。op 的问题本质就是 Promise ,并不是 async 和 await 。async 和 await 只是一个语法糖而已。 |
12 foolishcrab 107 天前 via iPhone ![]() 拿这个面试还不如直接问对方你们公司厕所纸用的什么品牌,这必须对你们公司有强烈意向以及惊人的观察力才能回答出来。对吧? |
![]() | 13 fgwmlhdkkkw 107 天前 ![]() 茴有几种写法? |
14 fpk5 107 天前 ![]() 中文提问 claude opus 4 回答不行,英文提问妙答正确答案。看来中文的训练语料库差距有点大,语言在 AI 时代仍然是个隔阂。 |
15 wefgonujnopu OP @dcsuibian 面试题不就是徒增麻烦的,手写 promise 没见过吗,有什么用,async await 本质上是语法糖,await xxx 实际上执行的就是 xxx.then(()=>{}),理解这个就能写出来,某些人学 promise 可能以为是线程,在阻塞的方法前面加 await 以为能异步的人也不少 |
16 wefgonujnopu OP @foolishcrab 对的,你们公司要招聘厕纸人才的话确实可以这样问 |
17 wefgonujnopu OP @lscho 确实是这样实现的,不过没有 await,用起来是回调地狱,这个考验你对 await 的理解而已,不是关键词,而是语法糖 |
18 Sunzehui 107 天前 什么叫“让当前 async 函数进入等待,不能阻塞线程”?你听听说的是人话吗,真不怨 AI 答不出来,难道 Javascript 是多线程语言? |
19 wefgonujnopu OP @Sunzehui 懂了,js 没有线程,什么函数都不会阻塞线程,单线程语言没有线程的概念,怎么写都不会卡死,即使是 while 死循环 |
![]() | 20 windliang PRO @wefgonujnopu #3 ChatGpt 4o 答案直接出来了 |
21 wefgonujnopu OP @windliang gpt 还是厉害的,我试了 grok 不行 |
![]() | 22 june4 107 天前 感觉是从别的会阻塞的真线程语言刚学 js 不到 10 分钟会问出来的问题 |
23 wefgonujnopu OP @june4 你怎么知道,我刚学 js 十秒 |
![]() | 24 passion336699 107 天前 之前看的一本什么书上讲过 “鸭式辨型”, 函数实现 thenable ,遵循 Promise A+,看起来像鸭子,那它就是一个鸭子 |
![]() | 25 songray 107 天前 手搓 Promise 是一道常见的面试题。 https://febook.hzfe.org/awesome-interview/book1/coding-promise |
26 Cbdy 107 天前 ![]() var Promise = require("bluebird"); |
27 felbryiozzzz 107 天前 ![]() 2025 年了还在自嗨这问题 哈哈哈哈 |
28 burnsby 107 天前 ![]() 会实现个这个就是大神了?这跟以前的'手撸一个 Promise'的问题有啥区别?知道这个就是大神了,那大神是多没有牌面? |
29 wefgonujnopu OP @burnsby 所以我什么时候说知道这个就是大神了,我说的是知道这个就理解 promise 了而已 |
30 wefgonujnopu OP @felbryiozzzz 抱歉,忘记看日期了,2025 是出了什么新规定禁止讨论 promise 吗 |
31 burnsby 107 天前 ![]() @wefgonunopu ```有没有人会写的,看看 v 站有多少大神```不是你的第一句话?掘金这种地方比较适合你,去吧 |
![]() | 32 xiangyuecn 107 天前 ![]() @songray #25 面试搞的就像上古时期要求兼容 IE6 一样 |
33 wefgonujnopu OP @burnsby 是啊,大神肯定会写啊,但是会写不一定是大神,逻辑学不好的话建议去学下 |
34 mx1700 107 天前 via Android ![]() 啊???我还以为有啥高深的理解,仅仅是一个 Thenable ???稍微深入一点也应该是仅用 yield 怎么实现,再深入一点是连 yield 也用怎么实现 |
![]() | 35 slowgen 107 天前 ![]() 没啥意义,qwen3-30B-A3B 的 4bit 量化在 M2 Ultra 上以 85 token/s 的速度秒了这一题,显存占用 18GB 左右。 prompt:从架构师角度分析这个问题"js 不使用 promise 完成一个 sleep 函数,必须支持 await 调用,还要能在浏览器使用",本地不跑大模型可以去官网 https://chat.qwen.ai/ 问 当你掌握很多门语言之后,就知道那些屎一样的临时过渡方案就知道压根没必要看,Javascript 的 async/await 都是抄 2012 年 C#发布的 5.0 语法,而且还没一次性抄对,中间搞那个 yield 恶心方案和过渡的 promise 方案,当时很流行的库有 co/bluebird/async 不知道有多少人记得。到了 2017 年 6 月 async/await 才正式并入规范,而 2017 年 5 月 Node.js 8.0 都正式支持 async/await 了,之前 6.x 都能通过参数开实验性支持了。 我从 8.0 开始用 Node.js ,当时直接上 TypeScript 写后端,根本懒得吃那些设计缺陷造的屎,原型链和这种 then 地狱风格的代码压根没写过一行,项目规范都禁止写这种代码。 至于你补充那个 python 例子,和异步也没啥关系,就一个默认递归深度,在带 GC 的语言中敢写递归之前不看限制或者有没有尾递归优化吗? |
36 wefgonujnopu OP @mx1700 要支持 await ,肯定只能 thenable,yield 是做不到的 |
![]() | 37 Imindzzz 107 天前 ![]() 感觉是为了这瓶答案包了这顿题目。。。 想解决爆栈,用 setTimeout 或者尾调递归都可以的。 |
38 kneo 107 天前 via Android 深入理解茴字的第二种写法。 |
39 wefgonujnopu OP @shuimugan 是没啥关系啊,但是 js 里使用 await 之后确实不会堆栈溢出,重要的是这个,python 只是对比,正常来说都会堆栈溢出,所以 js 这点不一样 |
40 wefgonujnopu OP @Imindzzz 不是为了解决爆栈,只是研究下机制,而且 js 编译器没有实现尾递归,可以了解下 |
![]() | 41 lichuyi 107 天前 === 问我茴香豆的茴有几种写法 |
42 visper 107 天前 其实我觉得,知道 await 是在等待 Promise,是 promise 的语法糖,到这层就够开发用了。当然能手写一个 Promise 就更厉害点。至于 Thenable 这些。除了显得自己对 js 了解更多一点,其实没太多用处了。不了解也没什么。不过话说回来,招人么,同样的价钱,肯定先更厉害一点的。就像 java 那些,以前开始的时候,是大学毕业的随便问点 if else 什么的知道就行了,来了公司再学。现在呢,都想问 jvm 的了解程序啊 spring 源码啊的。 |
43 felbryiozzzz 107 天前 ![]() @wefgonujnopu 你如果是个技术向,你大可以去多研究算法/设计模式/编译这些更高级的领域,去做一些解决方案出来解决实际问题。不管是公司级别的还是前端通用的领域 新语法出来是解决开发效率的,promise 给你了,async 给你了,规范制定者想,两者相结合异步的问题总该是都能解决了吧?好家伙,总有些大聪明想着我再用旧语法给你实现一遍,搞得我多理解这个语言语法一样。要是自己研究过了挺有成就感,还拿去面试别人,那只能说是真高人了 |
44 burnsby 107 天前 @wefgonujnopu 我要笑死了,你问题里的不是`看看 v 站有多少大神会写的`,还搁这逻辑学呢。 来,再给你贴个 AI 的回答。[img]( https://imgur.com/RNXDUyj) |
45 wefgonujnopu OP @felbryiozzzz 抱歉,这就是新语法,await 没有旧语法,这只是另一种实现方式,旧的是 settimeout 回调 |
![]() | 46 SanjinGG 107 天前 via Android 又是这种 nc 面试题,如果 js 没有问题你问个瘠薄?你是面 js 还是面 python ? |
47 wefgonujnopu OP @burnsby 我问的是,有没有人会写的,看看 v 站有多少大神,我有说会写=大神?你发的图都点不开,真的笑死了 |
48 wefgonujnopu OP @SanjinGG 就是 js 有问题,python 反而没问题,正常来讲都应该堆栈溢出,麻烦仔细看下 |
![]() | 49 EchoWhale 107 天前 via iPhone 什么场景下需要实现一个 sleep 但是不能用 promise ? |
50 wefgonujnopu OP @EchoWhale 高性能场景,只需要返回一个 thenable 对象,promise 对象需要维护的状态更多,占用的空间更多 |
51 Esec 107 天前 via Android 当时甚至还有专门写一个延迟多少秒才返回的服务端来做计时的玩笑,只希望不要有人真拿这个当知识来记 |
![]() | 52 SanjinGG 107 天前 @wefgonujnopu ? js 到底用不用 sleep ?如果用 sleep 有什么问题? |
53 wefgonujnopu OP @SanjinGG 和 sleep 没关系,是 await 的问题,每次调用都会把 await 下方的代码创建为任务,放到任务队列中,而不是从语言底层实现异步阻塞机制 |
54 realJamespond 107 天前 之前很多库的 promise 不是原生的 |
55 horizon 107 天前 @wefgonujnopu #48 是你臆想的高性能场景,还是实际使用这种方法提升了性能? |
![]() | 56 SanjinGG 107 天前 @wefgonujnopu js 单线程底层就是不支持系统级的 sleep ,你好像有点杠啊? |
![]() | 57 cxe2v 107 天前 八股文里的任务队列跟事件循环问题被你玩出花了,当赏 |
58 wefgonujnopu OP @SanjinGG 所以我什么时候说系统级了,我说的是语言底层,也就是 c++层?懂? python 的协程也是单线程,为什么可以? |
59 wefgonujnopu OP @horizon 测试了下,原生的 promise 反而性能更高,v8 引擎有优化,原生 promise 为 c++对象,在 nodejs 中差别很明显,原生的快 2.5 倍左右,不过浏览器里面基本没区别,所以这个只能作为兴趣研究下 |
60 oops2day 107 天前 |
![]() | 61 Vegetable 107 天前 ![]() 我最讨厌的就是用犄角旮旯的知识点考察面试者还沾沾自喜。尤其是这种只能考察出 知道或不知道 的谜语人问题,出现在 100 分的面试题中顶多值 2 分。 |
![]() | 62 nexo 107 天前 这个当面试题 面试的人真是倒了八辈子霉 到时候面试通过 去你代码库里这样写你又要不愿意了 |
63 wefgonujnopu OP @nexo 所以手写 promise 当面试题没问题吗,手写 promise 作为面试题,代码库里面就要用手写 promise? |
64 lianggggg 107 天前 前端是真的闲的 |
65 wefgonujnopu OP @EgoTao 递归有溢出确实是常识,但是 js 代码中 await 函数就会破坏这种常识,加了就永远不会溢出 |
![]() | 66 oubenruing 107 天前 @wefgonujnopu #50 有没有一种可能, 用了 await 但没有返回 promise 时,浏览器都会创建一个 promise 。 |
![]() | 67 |
68 penzi 107 天前 有一种大学生学 C 语言时候的热情。楼主没上过大学吧 |
69 wefgonujnopu OP @EgoTao 比如下面这个代码,去掉 await foo(),就会堆栈溢出,如果不去掉,程序会一直运行到出现 fatal error FATAL ERROR: Reached heap limit Allocation failed - Javascript heap out of memory async function foo() { return 1 } async function main(num = 0) { //去掉 sleep 会堆栈溢出 await foo() await main() } main() |
70 wefgonujnopu OP @Selenium39 面试题不都这样,比如自己实现 apply call bind 这些 |
![]() | 71 Torpedo 107 天前 ![]() 所以我一直都说,面试是看缘分的。 这样最好的标准就是 『你想不想和他一起工作』 |
72 icy37785 107 天前 看到 op 这种帖子,我一般都是直接 block 的。但是我还是耐着性子看了一下,op 底下的一些回复,确定看到这种帖子确实应该直接 block 。op 还臆想出了一种“高性能场景”,给我整不会了。 |
73 MzM2ODkx 107 天前 涨知识了,不过封装 sleep 函数不会比用 Promise 少多少字符 |
74 coderunI ![]() 所以,这段代码的深层意义在于: 极简实现:它用最少的代码实现了一个可被 await 的异步延时操作。 展示了 await 的本质:它清楚地表明了 await 的机制是基于 "Thenable" 接口的,而不是死板地绑定在 Promise 类型上。这对于深入理解 Javascript 异步编程规范( Promise A+ 规范)非常有帮助。 一种“炫技”或“教学”写法:在实际的团队项目中,为了代码的可读性和普适性,大家更倾向于使用 new Promise 的标准写法。而图片中的写法更像是一个精妙的例子,用来展示语言的内在机制。 总结 总的来说,这段代码的意义是: 在功能上,它提供了一个简洁的 sleep 函数来暂停异步代码。 在技术上,它是一个绝佳的范例,用最核心的方式展示了 await 关键字如何与任何拥有 then 方法的 "Thenable" 对象进行交互,而不仅仅是 Promise 对象。 |
75 FlashEcho 107 天前 本前端菜鸡根本就没看懂你的问题,new Promise setTimeout 不行吗? 稍微酷炫点的做法可以用原子量,不过一般 new Promise setTimeout 就够用了 |
76 wefgonujnopu OP @icy37785 所以你 block 之前还要回复下吗,上面已经说了,性能比原生的 promise 还慢,你确定耐着性子看了么,还是说也是自己臆想了下自己看过了,给我整不会了 |
![]() | 77 nexo 107 天前 @wefgonujnopu 我没考过一道这种 除非真的是做很底层的库开发 考这些可能会有点用 用来锻炼思维 |
![]() | 78 lyxxxh2 107 天前 |
![]() | 79 kdwnil 107 天前 via Android 高性能场景用 js ,这……对吗,自从发现 js 的时间间隔都做不到准确以后我就不在这种需求下玩弄 js 了 |
80 wefgonujnopu OP @kdwnil 对啊,你不知道 js 还有专门的 http 高并发优化的库吗,你小项目换语言容易,大项目可就要重构了,https://github.com/nodejs/undici |
![]() | nbsp; 81 senjyougahara 107 天前 @EchoWhale #49 面试,让你手动实现一个 Promise A+ ![]() |
82 FlashEcho 107 天前 @chesha1 #75 哦我没看标题,不允许用 promise 啊,那用原子量,也很方便的 ```Javascript async function sleep(ms) { const _sleepBuffer = new SharedArrayBuffer(4); const _sleepView = new Int32Array(_sleepBuffer); const { value } = Atomics.waitAsync(_sleepView, 0, 0, ms); await value; } ``` 至少表面上没有出现 promise |
83 wefgonujnopu OP @chesha1 可以,新答案,虽然用了 promise 对象,但是是 api 间接调用的,我还不知道有这个方法呢,看了下是新出的 |
![]() | 84 journalistFromHK 107 天前 哟吼 啥时候可以直接 return then 的 还是一直都可以? |
85 wefgonujnopu OP @journalistFromHK 一直都可以,任何对象上添加一个 then 方法,都可以 await |
86 wefgonujnopu OP @chesha1 你这函数貌似有问题,我试了 node22 运行立马就结束了, |
88 FlashEcho 107 天前 @wefgonujnopu #86 不清楚啊,我也只是知道这个概念,原子量可以做 sleep 这个事,自己实际上没用过,上面这个代码是 ai 生成的,自己没试过 |
![]() | 89 sss393 107 天前 这是在考我知不知道鸭子类型吗? JS 里只要实现 PromiseLike 就能被 await 吧。 题外话:有时候真的很反感 js 生态圈的各种八股文写法,有没有人知道 python 和 go 的生态圈难道也这样吗。。。。 |
90 wefgonujnopu OP @sss393 js 主要是历史遗留问题,刚开始是 promise 先出现,await async 还没有,后面出了,不过实现方式是语法糖,本质上还是 promise,就跟继承和 class 一样,本质都是 function,压根没有 class,类似的问题还有很多,比如 undefined 可以作为关键词,let undefined = 0 都可以,所以实际项目如果要写 undefined,规范点都会让你写 void 0 |
![]() | 91 unco020511 107 天前 感觉这个问题应该是个好问题,因为我这个前端初学者看不懂 |
![]() | 92 lyxxxh2 107 天前 ![]() @journalistFromHK await { then:(resolve) => resolve(1) } 接收一个对象,果然。 https://developer.mozilla.org/en-US/docs/Web/Javascript/Reference/Operators/await 文档都写的,很少人会过一遍文档的。 |
93 oops2day 107 天前 @wefgonujnopu #65 抛开 thenable 这个知识点跟你讨论哈,这个知识点你放我前几年刷八股文比较多的时候我大概率是能想到的,现在我确实没想到。 单说这个代码,就是我在业务中根本不会写出这种代码,那么它本身存在的意义并不大。如果放在 promise 规范里我觉得它也没有 promise 三个状态对于业务代码的帮助大。你下面列出来的代码,其实我在上面回复你之前自己验证过了,确实不会溢出,同样我也就比较好奇为什么不会溢出,还去 gpt 了一下。不会爆的本质就是 GC 了上一次的调用栈,所以理论上不会爆,整点闭包或者副作用多执行一会还是能爆的。 ![]() |
94 uchihaObito 107 天前 async function sleep (ms) { const now = performance.now() while (performance.now() - now < ms) {} return void 0 } |
95 lnbiuc 107 天前 带着答案找问题,没场景创造场景 |
96 wefgonujnopu OP @EgoTao 不会爆的本质是创建了微任务,而不是 GC 了,因为使用了 await,把下方的代码都包装为任务函数,添加到队列中稍后执行了,如果 GC 了,内存是不会溢出的,运行可以发现内存爆满,一瞬间几个 G ,业务中确实不会写出这种代码,因为不好维护,没意义,上面说了纯粹兴趣而已,就跟手写 apply call bind 和 promise 一样,也是面试题,但是实际项目不可能自己手写 |
97 wefgonujnopu OP @uchihaObito 你这个就是死循环了,阻塞线程的,sleep 过程中,进程直接卡死,所有异步任务停滞,而且 CPU 占用飙升 |
98 wefgonujnopu OP @lnbiuc 确实没有场景,因为是 await 原理探讨,手写 promise 也没有场景,b 站这个教程视频一堆人看呢,场景就是面试会问,除此之外没有任何场景 |
![]() | 99 hengshenyu 107 天前 这个问题对于老前端来说太简单了 |
![]() | 100 deadpl 107 天前 因为 Python 没有尾递归优化,导致栈空间溢出,跟协程没关系。 |