一直有个疑问,用 nodejs 时有什么优雅的办法能让代码在流程上回避掉回调吗?

2019-01-15 22:41:36 +08:00
 tomoya92

虽然现在 ES 标准里加上了 async await 来实现同步操作,还有 Promise,但业务复杂了,代码还是会被各种回调弄的执行流程出问题

举个例子

写 js 的时候用的最多的应该就是回调了,回调里可以传很多个参数,简单的操作,这样写很方便,但业务复杂了,就不方便了,回调地狱也就是这样来的

这时候你可能会说不是有 Promise 吗,但用这货我觉得也就是把代码变的好看些,拿结果还是要靠 then 方法回调拿

到这你可能还会说,不是还有 async await 吗,这货确实不用从 then 函数的回调里拿数据了,但用 nodejs 多了就会发现,很多函数的调用的写法还是会用到回调,而且这时候还会在回调函数的前面加上个 async,我也是无语了,这不是又回到起点了吗,如下

it ('waitBody', async function() {
	await driver.sleep(500).wait('body', 30000).html().then(function(code) {
    	isPageError(code).should.be.false;
    })
})

当然也有用起来舒服的地方,比如 mongoose 的查询

const results = await UserModel.find({});

综上,难道就没有一个优雅的方法能让代码一行一行的执行吗,前一个结果执行完了,拿到结果再参与下一行代码的执行?

有人会说了,你上面不是说了 async await 了吗,它不就是这样用的吗?那为啥还要在回调的方法上用 async await 呢?总觉得有点换汤不换药,折腾来折腾去,还是离不了回调,但回调又会涉及到代码结构和流程控制上的问题

还请原谅我这小白的问题,相信很多学习 nodejs 的朋友都有过这样的疑惑 😂

8183 次点击
所在节点    Node.js
80 条回复
momocraft
2019-01-16 10:52:18 +08:00
await 一个不 thenable 的表达式也是有作用的,await 后的代码一定在栈清空后才被执行 (和 promise 的 then 同样保证)

考虑:

```
async function foo() {
console.log(1);
await 0;
console.log(3);
}

foo();
console.log(2);
```

是不是有"意义"这个自己判断吧
janxin
2019-01-16 10:53:20 +08:00
@tomoya92 回调最大的问题难道不是很难看么
momocraft
2019-01-16 11:00:32 +08:00
回调的另一个问题是这个语法不自带 "可组合性",表现为层数多起来就很难连异常处理一起写对

而 await 时 promise fulfill 天然映射到表达式求值,promise reject 天然映射到求值中 throw,没被 catch 的 reject 会自动向上传递到一个无法忽略的地方
tomoya92
2019-01-16 11:04:03 +08:00
@janxin #42 还真不是,如果业务复杂,回调多了,会很乱的
lzvezr
2019-01-16 12:48:06 +08:00
@sagaxu 回调函数的循环控制嘛,这个在没有 await 的时候又不是没有

实际上 await 后面是一个 promise 对象,而 promise 对象要返回值给上层需要一个 resolve()作为回调函数,最终还是回调函数

这段程序要是没理解错的话,是需要依次获取数据直到得到正确结果
可以构造一个对象,包含当前执行到的位置(i),最多可以达到的位置(10),函数 f(this.i),每次 f 执行之后 this.i++或者 cb(ret.data)
Sparetire
2019-01-16 13:14:02 +08:00
@tomoya92 不要把 async/await 当成同步。。它们只是看起来同步,实际上还是异步,异步逻辑不会消失,只会看起来变得优雅。把 async/await 当成同步那会给自己挖坑的。。
sagaxu
2019-01-16 13:16:37 +08:00
@lzvezr 手动维护一个 context 心智负担太重,这只是一个简单的例子,循环可以有多层,调用链路可以有十几层,实际逻辑可能复杂的多,相当于人肉维护一个状态机。同步写法,思路顺畅许多,也更不容易出错。

不仅是 js,各种语言都在往弃回调,引入同步写法。
tomoya92
2019-01-16 13:22:00 +08:00
@Sparetire #46 也就是说用了 async/await 代码会在 await 那地方等着执行完对吧,至于被调用方法执行是同步执行还是异步执行的,不是 async/await 管的了,是这个意思不?
lamada
2019-01-16 13:25:52 +08:00
rxjs?
lzvezr
2019-01-16 13:48:31 +08:00
@sagaxu 嗯哼,好像理念并不冲突啊,谁不喜欢用同步写法去调用异步函数呢

楼主的问题是觉得已经有 async 了,结果使用第三方库只是回调函数从 function 变成了 async function,并没有解决回调

当其他库没法直接用,或者为了兼容性还在使用回调的时候,自己封装一下就显得很必要
reus
2019-01-16 13:57:51 +08:00
不要用 js 不就行了,找个对并发支持更好的不就行了
Sparetire
2019-01-16 14:03:31 +08:00
@tomoya92 只是当前函数的上下文中会等待在这里,但是每个等待的时候,都有可能存在其他函数在执行,和回调一样是异步的,这和同步是有区别的
est
2019-01-16 14:04:25 +08:00
优雅和 优雅 是互斥的。
est
2019-01-16 14:04:31 +08:00
优雅和 nodejs 是互斥的。
pkoukk
2019-01-16 14:09:33 +08:00
主要是很多第三方库是照着 promise 和回调设计的,所以有些地方没办法用 async/await 解决。
比如 sequelize 的 transcation
wly19960911
2019-01-16 14:22:08 +08:00
@reus #51 并发和 js 有什么关系嘛。并发是一个实现,js 只是工具,实际上异步实现并发性能还更好。如果是多线程的并发,还得玩多线程,相比异步反而麻烦了。
janxin
2019-01-16 14:30:58 +08:00
@tomoya92 乱不就是难看么...
oyjw443523
2019-01-16 14:38:12 +08:00
想不用回调就去试下 go,得用协程
yoshiyuki
2019-01-16 14:50:17 +08:00
异步回调是为了实现更好的 IO 性能,如果不需要 IO 性能可以考虑改用 PHP、Python 等
tomoya92
2019-01-16 14:56:46 +08:00
@oyjw443523 #58 go 用过,写着很舒服,不过还是没有 nodejs 用的广,而且公司要用啥也不是我说的算的 :joy

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

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

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

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

© 2021 V2EX