以前在本站看到过一个人贴了个特牛逼的网页,阻止用户打开控制台,标题忘了,可以搜搜
计算机的知识从来都是可深可浅。其实所有领域的知识都是这样,但因为计算机整个体系人造属性特别强,所以这个特征表现得更明显。
对于 async/await ,最简单的理解:某些函数需要加 await 才能调用,用到 await 的函数需要用 async 标记,而调 async 函数也需要 await 。
更深一点:await 的其实是一个 Promise 对象,所有 async/await 的代码都可以改写为 Promise 和 then 。还有个叫 Generator 的东西,它的思路和 Promise 完全不一样。更古早的代码里还有种写法叫回调函数。这四者可以相互改写。
为什么要这样?因为一些操作(如网络请求、读写文件)需要等待取回数据,在等待过程中可以让 CPU 做其他的事情提高效率,等到数据读完再回来执行下一步代码,所以有了回调函数,这种写法叫做异步。NodeJS 里有同步读写文件的方法,可以和异步写法对比感受差异。
因为浏览器环境强调实时响应,所以 JS 一开始就很重视异步,这也是其他语言容易实现 sleep 函数而 JS 很麻烦的原因。
为什么有这么多种写法?程序运行的一个本质问题是,下一步要执行什么。通常的逻辑是,如果是普通语句就正常执行,如果是函数调用就传参进入,如果是 return 则回到上一层调用继续执行。
假如所有代码都写到一个 C 函数里,那我们不需要考虑这么多复杂的问题,因为我们可以自由地自己做调度。但我们希望在保证代码结构性(拆成函数)的同时,能够继续保证效率(在等待网络响应时能执行别的代码)。对于有匿名函数的语言,最直接的想法就是回调函数,而且通常这个回调函数除了固定的参数外,还要能从发请求的地方抓一些变量写东西(这被称作闭包)。回调函数的思路和处理网页事件是一致的。
但回调函数的写法意味着,每次要发送请求或者干什么事情时,都要在上一层回调函数里再新建一层回调函数,缩进层次会特别深,这被称作回调地狱( callback hell )。更麻烦的是,真实代码的逻辑可能不是线性的,可能有一个 if 或循环,里面执行一些异步代码,然后再出来。这种时候写回调函数简直就是脑力大作战。
换种思路,我们能不能发明一种特殊的函数,比传统函数更强大?传统函数一旦 return ,只能再从头开始执行。我们想要的这个新函数,可以从中间返回,下次再从上次返回的地方继续执行。这样我们就不用写回调了,发请求的时候返回,等响应到了再跳回来就可以了。JS 本身就有闭包,且支持函数套函数(参考计数器的写法),这种功能理论上只需要实现一个返回函数的函数就行了,但 JS 还是有一个专门的语法,叫生成器( generator )。
而回调函数的写法也有改进空间。如果能把异步操作封装进一个对象里,然后动态添加每一层的回调,虽然还得写很多匿名函数,但缩进上至少可以拍平了。这就是 Promise ,它用 then 函数连接下一个回调。
如果再把一层层的 then 里的函数也抽出来,那 then 就变成了 await 。而对于用到了 await 的函数,它的执行流程和生成器就非常像了,在 await 的地方跳出去交还给 runtime ,等到合适的时候再跳回来。所以用到 await 的函数和生成器生成的函数一样,不是普通函数,必须要有区别,所以有 async 标记。但到这里应该能理解,这几种写法是等价的,await 一个 async 函数,和 await 一个返回 Promise 的普通函数,是一回事。
关于回调函数的讨论还可以更进一步。回调函数的写法,是把事件响应后要执行的代码封装成一个函数。但如果我把所有要执行的语句都写成这个样子呢?比如正常的 a=b+1;b=c+1 ,把后一句也写在一个回调函数里面会怎么样?除了极度蛋疼外,你也会获得极度的灵活性。
实际上,这个由接下来的代码组成的回调函数有个学名,叫 continuation (有时中文翻译作续体)。一些语言(如 Lisp 、Ruby )直接提供了操作 continuation 对象的能力,但这个功能太强大,所以衍生出一种弱一些的变体,叫 delimited continuation ,也就是需要指定这个接下来执行的函数到哪为止。Kotlin 里面带 @ 符号的 return 就有点这个味。
而 async/await 的语法也可以进一步泛化。await 的逻辑是跳出去再跳回来,形式上是否和异常的 try catch 有点像?特别某些语言的异常还真的支持 retry 语句。还真的有种语法概念把 await 和异常统一起来,叫 algebraic affects 。所以 await 真的没什么神秘的。
到这一步其实还有很多问题没有深入,比如这些 runtime 在操作系统和原生代码层面是怎么实现和封装的,其他模型比如 go 的 goroutine 和传统的多进程模型等。但我想你现在理解 async/await 一定容易了很多:)
那是因为用中文还能搜到东西,日语、西班牙语、俄语这些还能有内容,使用人数更少的语言就真的和计算机绝缘了。中文计算机内容有内容农场污染(但这个英文也有),独立博客少,更重要的原因或许是很多东西根本没人写。相比之下,知乎在计算机方面算得上不错的信息源,但现在也不行了。
令我比较感慨的一个时刻是,想搜 PL 相关的一些东西,知乎上有人讨论,但很多都是小圈子卖萌黑话,像是某群二次元爱好者的 play 。但我用英文搜索时,在 StackOverflow 上就有非常正经的、语言平实的回答和讨论,这对比比用中文啥也搜不出来更难受。
简单来说你的信任总是要落在某个基点的,如果你觉得苹果的 A 系列芯片有手脚,那什么操作都不安全。再往上是 iOS 、App Store 和聊天 App 本身。如果你愿意信任 App 那这个问题就有讨论的意义了。
Signal 支持让联系人线下相互扫码确认公钥没被篡改,到这一步如果服务器也不记录历史消息,那基本可以确认为安全。
iMessage 因为太黑盒了,苹果说的话很暧昧,也不开源,所以无法判断。但至少可以确定,开启了 iCloud 同步的 iMessage ,不咋安全。
反诈宣传各种暗示你关闭 FaceTime 和 iMessage ,但他们明明有能力直接墙掉,这很奇怪
有一个为苹果不给 iPhone 加内存的辩解:如果今年 8G ,明年 12G ,那老机型只会被淘汰得更快。某种思路上看,这个说法还有点道理
Ruby 适合创业,国内创业不行了 Ruby 就不行了
cpp 的心智负担是重,但你说的右值引用和移动构造这些东西并不是 cpp 的心智负担造成的,甚至不是因为 cpp 没有 gc 造成的,所有支持值语义的语言都必须实现类似的概念。
一个人觉得残忍是因为他作为个体在公司面前没有议价能力,只能成为赤裸裸内卷的代价。而如果一个人认为自己有议价能力,这种模式就会显得非常美好,说白了就是咨询。
《程序员的软技能》这本书里提到一种思维模式,即打工人不要想象自己是就职公司的一分子,而是自己开了一家一个人的公司,然后把当前雇主当成自己唯一的客户。这个思路很有意思。
Mac 和 iOS 以及 Linux 普遍支持 Emacs 风格快捷键,所以还好。用 HHKB 更麻烦的其实是 Esc 和~,这两个键合二为一很不方便
麻了,一直没更新,结果昨天不知道怎么着触发了更新,晚上 12 点睡觉时有 50%的电,起来发现手表没电了,看电池记录 4 点就把电用完了,然后现在还在充着电准备
连 Combine 这种几乎不依赖什么系统功能的库苹果都要搞成 iOS 13 可用,摆明了就是故意不兼容,除了 iOS 15 的 async/await 开发者民怨太大才往后兼容回了 13 。其实 SwiftUI 本身都能搞成开源的
去年搞过一段时间黑苹果,最后放弃了。可能因为我不是完美配置,只是拿现有的一台 AU A 卡的机器弄的,问题一是无法睡眠,二是时常无法正常启动,但可能过段时间多试几次又可以了。如果当一台不开机的服务器还可以,但只当服务器那就还不如 Linux 。鉴于苹果内存和硬盘卖得过于贵,黑苹果还会继续有存在的空间,但有钱还是上白苹果,舒心不少。
M3 系有明显提升,建议再等等,哪怕等 M3Max 出了等 M2Ultra 降价也是好的。