agagega

agagega

V2EX 第 302609 号会员,加入于 2018-03-22 23:16:35 +08:00
今日活跃度排名 2785
根据 agagega 的设置,主题列表被隐藏
二手交易 相关的信息,包括已关闭的交易,不会被隐藏
agagega 最近回复了
12 小时 31 分钟前
回复了 haha922 创建的主题 分享发现 远离阿里有感,也应该远离付费 app 国内个人开发者
有点以偏概全了,但远离那些戾气重在推特上还一堆人捧臭脚的国内网红独立开发者确实没错😁
13 小时 10 分钟前
回复了 Mark24 创建的主题 问与答 如果不考虑赚钱(你有无限的钱),你最想投身做什么
我记得 tombkeeper 发过一条微博,大意是如果他有那么多钱和资源,会资助策划一个小国家的政变,然后扶植一个自己指定的人上台,做社会实验。

我没有那么深的想象力,但大量资金足够做一些影响社会观念的东西了。再浅薄一点就是局限在 IT 领域内,资助一批自己想做的开源项目,撬动整个业界。Telegram 其实就有点这个意思,而且可以说已经实现目标了。如果比 Telegram 创始人的钱还要多,那可以做的事情显然更大。
计算机的知识从来都是可深可浅。其实所有领域的知识都是这样,但因为计算机整个体系人造属性特别强,所以这个特征表现得更明显。

对于 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 上就有非常正经的、语言平实的回答和讨论,这对比比用中文啥也搜不出来更难受。
1 天前
回复了 pxiphx891 创建的主题 Apple 关于 iMessage 端到端加密,我有一个问题
简单来说你的信任总是要落在某个基点的,如果你觉得苹果的 A 系列芯片有手脚,那什么操作都不安全。再往上是 iOS 、App Store 和聊天 App 本身。如果你愿意信任 App 那这个问题就有讨论的意义了。

Signal 支持让联系人线下相互扫码确认公钥没被篡改,到这一步如果服务器也不记录历史消息,那基本可以确认为安全。

iMessage 因为太黑盒了,苹果说的话很暧昧,也不开源,所以无法判断。但至少可以确定,开启了 iCloud 同步的 iMessage ,不咋安全。
1 天前
回复了 pxiphx891 创建的主题 Apple 关于 iMessage 端到端加密,我有一个问题
反诈宣传各种暗示你关闭 FaceTime 和 iMessage ,但他们明明有能力直接墙掉,这很奇怪
有一个为苹果不给 iPhone 加内存的辩解:如果今年 8G ,明年 12G ,那老机型只会被淘汰得更快。某种思路上看,这个说法还有点道理
8 天前
回复了 cxhello 创建的主题 Ruby on Rails Ruby on Rails 国内使用情况
Ruby 适合创业,国内创业不行了 Ruby 就不行了
10 天前
回复了 OliverDD 创建的主题 程序员 对数据库感兴趣,但完全接受不了 cpp
cpp 的心智负担是重,但你说的右值引用和移动构造这些东西并不是 cpp 的心智负担造成的,甚至不是因为 cpp 没有 gc 造成的,所有支持值语义的语言都必须实现类似的概念。
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2815 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 11ms · UTC 05:52 · PVG 13:52 · LAX 22:52 · JFK 01:52
Developed with CodeLauncher
♥ Do have faith in what you're doing.