一直有个疑问,用 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 的朋友都有过这样的疑惑 😂

8173 次点击
所在节点    Node.js
80 条回复
motai
2019-01-16 08:08:30 +08:00
回调是 js 的精髓吧
lzvezr
2019-01-16 08:24:24 +08:00
回调是不可能避免的,只是封装成 promise,让.then 或者 await 调用而已
.then 和 await 只是为了让代码变得好看
比如
await cb1()
await cb2()
await cb3()
要比
f()
.then(cb1())
.then(cb2())
.then(cb3())
或者
f(cb3(cb2(cb1)))
好看一些
tomoya92
2019-01-16 08:58:16 +08:00
@lzvezr #22 我也是觉得 async await 这种就是让代码好看的,还是没有解决回调问题
des
2019-01-16 09:04:33 +08:00
那么,你可以去看看 fibjs
另外你可以用 promise 包装回调,然后 await
然而我觉得是你没明白 promise 和 async 是做了什么
shintendo
2019-01-16 09:06:03 +08:00
@tomoya92
我也是觉得 async await 这种就是让代码好看的,还是没有解决回调问题
----------------------
因为回调本来就不是问题啊,nodejs 的精髓你要把它“解决”掉,那为什么要用 nodejs 呢
lhx2008
2019-01-16 09:08:36 +08:00
还有一种方法是响应式编程,rxjs 或者 reactorjs,不过也只是看起来比较美好,await 那种还是好用一点
tomoya92
2019-01-16 09:09:07 +08:00
@des #24 我确实是糊里糊涂的
tomoya92
2019-01-16 09:10:20 +08:00
@lhx2008 #26 嗯,谢谢,我觉得我还是像楼上说的那样,先把 promise async await 它们都是做什么的弄清楚比较好
woodensail
2019-01-16 09:14:02 +08:00
ps:async await 可不只是好看一点这么简单。
async 给了你统一的错误流,以及可精确控制范围的 try catch。
没有 async 前,写复杂异步链的异常处理非常之痛苦……
learnshare
2019-01-16 09:43:53 +08:00
Promise + async/await 已经比较优雅了
qiushijie
2019-01-16 09:47:54 +08:00
异步适用一次回调,回调使用多次调用
wunonglin
2019-01-16 10:00:28 +08:00
用了 await 还用 then ??
tomoya92
2019-01-16 10:02:28 +08:00
@tcdw #6 大佬,再打扰一下,看你给的链接里的介绍,`fs.stat` 方法传进 `util.promisify(fs.stat) ` 里当参数后,下面就可以直接使用 async/await 来同步执行拿返回值数据了,当然它们有规则,就是 fs.stat 方法的回调第一个参数要是异常

这样看来我是不是就可以理解为,任何一个被包装成了 Promise 的函数都可以使用 async/await 来同步执行拿返回值的数据?

另外 `util.promisify()` 方法跟自定义的 Promise 封装是不是效果一样的呢?比如下面这两种用法是等效的吗?

```js
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}

timeout(100).then((value) => {
console.log(value);
});
```

```js
const timeout = util.promisify(setTimeout);
timeout(100).then((value) => {
console.log(value);
});
```

谢谢!
cuberlzy
2019-01-16 10:21:59 +08:00
``` js

function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms);
});
}

await timeout(1000);

```
roscoecheung1993
2019-01-16 10:31:59 +08:00
这就是 nodejs 的特点...如果没有异步,单线程模型就无法支持多用户访问了。
某些模块的 api 还是支持同步调用的,比如 fs.readFileSync...写起来方便但是就阻塞了
momocraft
2019-01-16 10:36:01 +08:00
代码清楚就行了,async/await 只是语法糖,不用太敏感

await 之后的代码逻辑上已经是被 await 的 promise 的 then 的 callback,只是看起来代码是“连续”的
yamedie
2019-01-16 10:36:02 +08:00
@tomoya92 "任何一个被包装成了 Promise 的函数都可以使用 async/await 来同步执行拿返回值的数据?"
await 期待的就是一个 promise
(如果 await 后面跟的是一个普通的 function, 也不会报错, 只是 await 变得毫无意义了)
tomoya92
2019-01-16 10:39:14 +08:00
@yamedie #37 哦哦,明白了,那我理解的就是对的了,谢谢!
momocraft
2019-01-16 10:41:24 +08:00
顶楼的代码是不是也可以写成

```
const code = await driver.sleep(500).wait('body', 30000).html();
isPageError(code).should.be.false;
```

? 这样会清楚点吗?
sagaxu
2019-01-16 10:48:34 +08:00
@lzvezr 这段代码用回调写写看,就知道 await 是不是好看了

for (let i = 0; i < 10; i++) {
const ret = await $.getData(i);
if (ret.code !== 0) {
continue;
}

if (await check(ret.data) === true) {
return ret.data;
}
}

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

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

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

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

© 2021 V2EX