关于 async function 的设计哲学:如何评价 await 一个 resolved 的 promise 会使上下文拆成两个 tick ?

2017-02-13 21:05:54 +08:00
 dou4cc
4451 次点击
所在节点    JavaScript
49 条回复
noli
2017-02-14 09:31:04 +08:00
@dou4cc 你的缓存回收是纯内存操作?这个缓存回收是在异步操作里面完成?为什么要这样做?
fszaer
2017-02-14 09:32:27 +08:00
请问以下例子与 po 主所示代码中的异同
let s=0;
(()=>{
setTimeout(()=>s++,0);

})()
++s;
xialdj
2017-02-14 09:33:45 +08:00
a();p.then(b);c(); 如果 p 已经被 resovled 那楼主的理解是不是执行顺序为 a b c ? 这个问题本质上和 await 是一致的
jkeylu
2017-02-14 09:51:13 +08:00
楼主想要的是下面这样的效果吗?其实调用 async 函数本质就是返回一个 promise
async function foo() {
let s = 0;
await (async () => {
await Promise.resolve();
++s; //2
})();
++s; //1
}

foo();
lujinang
2017-02-14 09:54:07 +08:00
@dou4cc 你根本没理解 node 的异步机制
morethansean
2017-02-14 09:59:06 +08:00
await 怎么对 tick 有什么浪费了……
本质上这里涉及到的还是 Promise 的概念, Promise 的 then(f, r) 保证了 f 一定不是立即执行的(看起来像是异步),而是被立即入队。要是 then 一会儿是同步的一会儿是异步的,这对整个系统来说都是灾难好吗?
你都知道 async 关键字的意义,那么如果一定要实现你想实现的,肯定要把你的整个状态机都包含在 async function 内部啊……你想要用 async 的语法糖来让你的代码更方便一些,一会儿又在语法糖外面抱怨语法糖。你所谓的性能影响是因为你用了错误的方式造成了一些“意料之外”的结果,光是 then 将 f 入队本身没有什么严重的开销。
dou4cc
2017-02-14 10:39:29 +08:00
@morethansean 我觉得 async function 应该节省 tick ,另实现一个时而异步时而同步的 then2 。健壮的程序可以应对时而同步时而异步的回调。
dou4cc
2017-02-14 10:43:48 +08:00
@morethansean tick 本身确实开销不大,但节省 tick 可以带来的好处很多,除了我说的缓存机制,还有很多卡 tick 的事可以做
otakustay
2017-02-14 11:03:10 +08:00
jQuery 的 Deferred 最初就是你的这个模式,如果已经 resolved 则是同步的,否则会变成异步
但是这显然是不行的,从语言来说最重要的是一致性,即一个 Promise 你不应该需要知晓其当前状态(你看 Promise 本身也没有一个字段让你读状态),其当前状态应该不会影响你的任何逻辑,所以无论是否 resolved 其都必须是异步的
morethansean
2017-02-14 11:13:07 +08:00
@dou4cc 还是那句话啊,你大概在理解上有一点偏离了……

Promise 的设计本来就是需要保证异步的,不然这对使用者来说有极大的不稳定性需要考虑过于复杂的情形,甚至在有些场景下这将对整个代码结构都带来灾难性的破坏。 Promise then 本来就不是阻塞的不是同步代码,本来就是你所谓的 "tick" 模式。

then 把一个任务加入了队列,你的外部世界的语句本来就没处在 Promise 的范畴内,自然和这个任务也没有关系。

我们来看看你想要的结果,你想要的是:

- 对一个 Promise 调用 then(f),如果这个 Promise 已经 resolve 了,那么 f 是立即执行的而不是被加入队列等当前的任务完成再执行。

如开头所说不这样的原因是,这让 Promise 变得不确定,实际生产中这会带来很大的问题( V2EX 上就会有一大堆人开始批判这个坑),没人会想用 Promise 。而且说实话,一个函数一会儿同步一会儿异步,这很怪异,我没见过。

可是你偏想要这么做,要提供一个 then2 ,满足这样的效果可以么?可以,当然可以啊(扩展一个 Promise 类,保存 Promise 的状态,调用 then 的时候检查一下然后做处理。对于 await ,虽然可能需要自己写处理器 polyfill 一下,在真正调用 await 之前检查一下 Promise 的状态),可是你真的需要这么做么?这就是大家关注的问题所在,我们可能认为你本来并不需要这样做(比如楼上有人提到你可能需要一个全局的 async 等等),所以认为你的理解是有偏差的。

你所谓的各种卡 "tick" 的好处,不是 then(f) 可以一会儿同步一会儿异步的理由。
fds
2017-02-14 11:28:53 +08:00
前面 @jkeylu 说的对,想保证执行顺序就是

let s = 0;
await (async () => {
await Promise.resolve();
++s; //1
})();
++s; //2

不过这跟 tick 没有关系,肯定是多个 tick 才能完成。
Premature optimization is the root of all evil -- DonaldKnuth
rogerchen
2017-02-14 11:33:45 +08:00
明明是同步逻辑非要写成异步有什么办法。 await 了不交控制流出去难道阻塞着过年?
dou4cc
2017-02-14 12:10:22 +08:00
@otakustay 我和你对一致性的理解有分歧,我认为健壮的程序无需 then 旳异步保证、同异步无本质区别。更显然地如果 then 实现成我说的,改成现在的会很方便:
Promise.all([p, new Promise(r => setTimeout(r, 0))]).then
即可,而从现在的实现改成我说的,就只能放弃 async function 改用 generator function 了
otakustay
2017-02-14 12:14:57 +08:00
@dou4cc 然而你想要的永远不会和任何语言的设计原则相符合,这就是现实
otakustay
2017-02-14 12:25:24 +08:00
最简单的例子,把你的代码改成一个比较现实的代码:

(async () => {
let user = await fetchCurrentUserInfo();
console.log(1);
})();
console.log(2);

请问你认为这个代码应该是 1 - 2 还是 2 - 1 还是任意都可以?
limhiaoing
2017-02-14 13:11:58 +08:00
@morethansean
一会异步一会同步的你没见过不代表没有, C#的 async await 就是这样的。
limhiaoing
2017-02-14 13:21:39 +08:00
C#之类的这么做是从性能的角度考虑已经完成的同步执行性能会更好,比如从 socket 读一段数据,这个操作可能可以立即完成也可能无法立即完成。对于可以立即完成的完全可以同步执行,对于无法立即完成的才异步。
至于 node 为什么选择全异步就了解了。
limhiaoing
2017-02-14 13:22:20 +08:00
@limhiaoing 打错,最后一句少打了个不字。
morethansean
2017-02-14 13:30:14 +08:00
@limhiaoing 兄弟你要结合上下文不是这么断章取义的,我说的是作为 Promise 的一个 API , then 的执行一会儿是同步一会儿是异步。

给你一个 API readFile 然后告诉你这个 API 有可能是同步的有可能是异步的?

写 Promise 链的时候就不会关心你的业务逻辑具体是怎么执行的,你从缓存里直接读也好你发请求异步读也好,只是在我 method chain 中的一环而已我还要去关心你是同步异步的具体做了什么事?而且这里还并没有显示能够看到是同步还是异步的方法,只是做了一个函数调用而已。

Promise 本来是用来干嘛的,要解决什么问题,所以他这么设计了,把不稳定性和混乱引进来强行可以卡 tick 做很多的事情,关键是这些事情本来不是这么去做的。
morethansean
2017-02-14 14:02:58 +08:00
@limhiaoing 而且由于我不太清楚 C# 里面的 async/await 的实现,然而我简单地搜索了一下,包括 http://developer.51cto.com/art/201305/393992_all.htm 这篇文章,这样如果抛开 nodejs 和 C# 单 /多线程的区别的话,看起来并没有什么不同。文章中有提到 `await 关键处的代码片段是在线程池线程上执行` 并不是 GUI 线程。再者,搜了一下知呼,里面也提到 await 开始的那一刻 async 函数就返回了,其实都是编译器的语法糖而已。是我理解有问题吗……感觉不是很懂你的意思了……

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/340231

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX