虽然现在 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 的朋友都有过这样的疑惑 😂
1
oott123 2019-01-15 22:46:54 +08:00 via Android 1
没有,这就叫异步,你可以改用 Python。
|
2
sagaxu 2019-01-15 22:48:41 +08:00 via Android
promise 不能 await?
|
3
lekai63 2019-01-15 22:51:14 +08:00 via iPhone
async 都不能忍受的话 要不考虑学个正经的偏后端语言?
|
4
misaka19000 2019-01-15 22:51:15 +08:00
最好的办法就是不写 JS (滑鸡)
|
5
EPr2hh6LADQWqRVH 2019-01-15 22:52:25 +08:00 via Android
promisify
|
6
tcdw 2019-01-15 22:56:45 +08:00 via Android
|
7
Hanggi 2019-01-15 22:57:40 +08:00
你这个写的感觉有问题。你再好好看看。
|
9
wly19960911 2019-01-15 23:07:43 +08:00
这个和 nodejs 有什么关系呢,那异步你怎么解决异步回调的问题? async 已经很接近同步的写法了。
不是 nodejs 想这么做,而是逻辑上只有这样的处理啊,不断的回调,我知道你什么时候运行什么方法嘛?我只能等你运行完成之后调用我才知道,你可以选择同步,但是问题是同步又阻塞线程你满意嘛。。 另外你用 await 就用错了,是我我会这么用,虽然我接触 await 是在 flutter 里面。 it ('waitBody', async function() { var a = await driver.sleep(500); var b =await a.wait('body', 30000); await html(); (function(code) { isPageError(code).should.be.false; })(); }) |
10
tomoya92 OP @Hanggi #7 前端萌新,一直都是迷迷糊糊的用 nodejs,最近觉得非常有必要把这个弄清楚,所以才发帖求助的,还请见谅 :joy
|
11
tomoya92 OP @wly19960911 #9 感谢指点,上面那段代码不是我写出来的,是 uirecorder 录制完成后自动生成的测试用例里的代码,我也觉得在回调上还用 async await 总觉得别扭 :joy
|
12
wly19960911 2019-01-15 23:10:44 +08:00
async 和 await 是把一切耗时的操作打上一个 await,然后整体下来看起来就像同步,而不是像你那样 promise 和 async 混搭
|
13
Pastsong 2019-01-15 23:13:44 +08:00 via Android
js 语言特色就是单线程异步,你要先理解 async 解决了什么问题
|
14
tomoya92 OP @wly19960911 #12 await 不是等待的意思吗?加上这个关键字后,后面的代码要等待这行代码执行完才往下继续执行,不应该是这个意思吗?
|
15
wly19960911 2019-01-15 23:18:55 +08:00 1
@tomoya92 #14 是的,然后你就没必要去不断的 写个 promise.then()去处理回调了,把 promise.then()的东西丢到一个方法里面,一个个 await 调用,实际上会比 promise.then()好看,而且逻辑上清晰。async 没有解决什么根本上的问题,只是一种新的写法让异步回调看起来像同步,把箭头函数里面的东西丢到一个封装方法里面,最后到达一个更直观的代码.
|
16
Sparetire 2019-01-16 01:55:05 +08:00 via Android
没看懂都 await 了,为什么还要 then。。讲道理是不会出现一个 then 的
|
17
autoxbc 2019-01-16 04:42:41 +08:00 1
这个例子本身写的不好,看面条代码脑子绕圈是正常反应,要先坚实自己,就不会被带偏
可以看 Promise 迷你书 |
18
omnip 2019-01-16 04:51:51 +08:00
试试这样
const code = await driver.sleep(500).wait('body', 30000).html(); isPageError(code).should.be.false; |
19
edward8628 2019-01-16 05:31:48 +08:00
async 和 await 是目前最优雅的了
|
21
motai 2019-01-16 08:08:30 +08:00 via iPhone
回调是 js 的精髓吧
|
22
lzvezr 2019-01-16 08:24:24 +08:00 via Android
回调是不可能避免的,只是封装成 promise,让.then 或者 await 调用而已
.then 和 await 只是为了让代码变得好看 比如 await cb1() await cb2() await cb3() 要比 f() .then(cb1()) .then(cb2()) .then(cb3()) 或者 f(cb3(cb2(cb1))) 好看一些 |
24
des 2019-01-16 09:04:33 +08:00 via Android
那么,你可以去看看 fibjs
另外你可以用 promise 包装回调,然后 await 然而我觉得是你没明白 promise 和 async 是做了什么 |
25
shintendo 2019-01-16 09:06:03 +08:00 1
@tomoya92
我也是觉得 async await 这种就是让代码好看的,还是没有解决回调问题 ---------------------- 因为回调本来就不是问题啊,nodejs 的精髓你要把它“解决”掉,那为什么要用 nodejs 呢 |
26
lhx2008 2019-01-16 09:08:36 +08:00 via Android
还有一种方法是响应式编程,rxjs 或者 reactorjs,不过也只是看起来比较美好,await 那种还是好用一点
|
28
tomoya92 OP @lhx2008 #26 嗯,谢谢,我觉得我还是像楼上说的那样,先把 promise async await 它们都是做什么的弄清楚比较好
|
29
woodensail 2019-01-16 09:14:02 +08:00 1
ps:async await 可不只是好看一点这么简单。
async 给了你统一的错误流,以及可精确控制范围的 try catch。 没有 async 前,写复杂异步链的异常处理非常之痛苦…… |
30
learnshare 2019-01-16 09:43:53 +08:00
Promise + async/await 已经比较优雅了
|
31
qiushijie 2019-01-16 09:47:54 +08:00 via Android
异步适用一次回调,回调使用多次调用
|
32
wunonglin 2019-01-16 10:00:28 +08:00
用了 await 还用 then ??
|
33
tomoya92 OP @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); }); ``` 谢谢! |
34
cuberlzy 2019-01-16 10:21:59 +08:00
``` js
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); } await timeout(1000); ``` |
35
roscoecheung1993 2019-01-16 10:31:59 +08:00
这就是 nodejs 的特点...如果没有异步,单线程模型就无法支持多用户访问了。
某些模块的 api 还是支持同步调用的,比如 fs.readFileSync...写起来方便但是就阻塞了 |
36
momocraft 2019-01-16 10:36:01 +08:00
代码清楚就行了,async/await 只是语法糖,不用太敏感
await 之后的代码逻辑上已经是被 await 的 promise 的 then 的 callback,只是看起来代码是“连续”的 |
37
yamedie 2019-01-16 10:36:02 +08:00
@tomoya92 "任何一个被包装成了 Promise 的函数都可以使用 async/await 来同步执行拿返回值的数据?"
await 期待的就是一个 promise (如果 await 后面跟的是一个普通的 function, 也不会报错, 只是 await 变得毫无意义了) |
39
momocraft 2019-01-16 10:41:24 +08:00
顶楼的代码是不是也可以写成
``` const code = await driver.sleep(500).wait('body', 30000).html(); isPageError(code).should.be.false; ``` ? 这样会清楚点吗? |
40
sagaxu 2019-01-16 10:48:34 +08:00 via Android
@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; } } |
41
momocraft 2019-01-16 10:52:18 +08:00 2
await 一个不 thenable 的表达式也是有作用的,await 后的代码一定在栈清空后才被执行 (和 promise 的 then 同样保证)
考虑: ``` async function foo() { console.log(1); await 0; console.log(3); } foo(); console.log(2); ``` 是不是有"意义"这个自己判断吧 |
43
momocraft 2019-01-16 11:00:32 +08:00 1
回调的另一个问题是这个语法不自带 "可组合性",表现为层数多起来就很难连异常处理一起写对
而 await 时 promise fulfill 天然映射到表达式求值,promise reject 天然映射到求值中 throw,没被 catch 的 reject 会自动向上传递到一个无法忽略的地方 |
45
lzvezr 2019-01-16 12:48:06 +08:00 via Android
@sagaxu 回调函数的循环控制嘛,这个在没有 await 的时候又不是没有
实际上 await 后面是一个 promise 对象,而 promise 对象要返回值给上层需要一个 resolve()作为回调函数,最终还是回调函数 这段程序要是没理解错的话,是需要依次获取数据直到得到正确结果 可以构造一个对象,包含当前执行到的位置(i),最多可以达到的位置(10),函数 f(this.i),每次 f 执行之后 this.i++或者 cb(ret.data) |
46
Sparetire 2019-01-16 13:14:02 +08:00 via Android
@tomoya92 不要把 async/await 当成同步。。它们只是看起来同步,实际上还是异步,异步逻辑不会消失,只会看起来变得优雅。把 async/await 当成同步那会给自己挖坑的。。
|
47
sagaxu 2019-01-16 13:16:37 +08:00 via Android 1
@lzvezr 手动维护一个 context 心智负担太重,这只是一个简单的例子,循环可以有多层,调用链路可以有十几层,实际逻辑可能复杂的多,相当于人肉维护一个状态机。同步写法,思路顺畅许多,也更不容易出错。
不仅是 js,各种语言都在往弃回调,引入同步写法。 |
48
tomoya92 OP @Sparetire #46 也就是说用了 async/await 代码会在 await 那地方等着执行完对吧,至于被调用方法执行是同步执行还是异步执行的,不是 async/await 管的了,是这个意思不?
|
49
lamada 2019-01-16 13:25:52 +08:00
rxjs?
|
50
lzvezr 2019-01-16 13:48:31 +08:00 via Android
@sagaxu 嗯哼,好像理念并不冲突啊,谁不喜欢用同步写法去调用异步函数呢
楼主的问题是觉得已经有 async 了,结果使用第三方库只是回调函数从 function 变成了 async function,并没有解决回调 当其他库没法直接用,或者为了兼容性还在使用回调的时候,自己封装一下就显得很必要 |
51
reus 2019-01-16 13:57:51 +08:00
不要用 js 不就行了,找个对并发支持更好的不就行了
|
52
Sparetire 2019-01-16 14:03:31 +08:00 via Android 1
@tomoya92 只是当前函数的上下文中会等待在这里,但是每个等待的时候,都有可能存在其他函数在执行,和回调一样是异步的,这和同步是有区别的
|
53
est 2019-01-16 14:04:25 +08:00
优雅和 优雅 是互斥的。
|
54
est 2019-01-16 14:04:31 +08:00
优雅和 nodejs 是互斥的。
|
55
pkoukk 2019-01-16 14:09:33 +08:00
主要是很多第三方库是照着 promise 和回调设计的,所以有些地方没办法用 async/await 解决。
比如 sequelize 的 transcation |
56
wly19960911 2019-01-16 14:22:08 +08:00
@reus #51 并发和 js 有什么关系嘛。并发是一个实现,js 只是工具,实际上异步实现并发性能还更好。如果是多线程的并发,还得玩多线程,相比异步反而麻烦了。
|
58
oyjw443523 2019-01-16 14:38:12 +08:00
想不用回调就去试下 go,得用协程
|
59
yoshiyuki 2019-01-16 14:50:17 +08:00
异步回调是为了实现更好的 IO 性能,如果不需要 IO 性能可以考虑改用 PHP、Python 等
|
60
tomoya92 OP @oyjw443523 #58 go 用过,写着很舒服,不过还是没有 nodejs 用的广,而且公司要用啥也不是我说的算的 :joy
|
61
tomoya92 OP @yoshiyuki #59 其实用 nodejs 还有一个好处,就是快,开发效率要比 java 不知道高多少 :joy
|
63
TomVista 2019-01-16 15:36:42 +08:00
```
function some (callback){ } function callback(callback2){ } function callback2(callback3){ } ... ``` |
64
Ritr 2019-01-16 16:44:50 +08:00
js 本身就是异步的呀,所以我打算学个其他语言
|
65
reus 2019-01-16 17:19:33 +08:00
@wly19960911 我用多线程从来就不用考虑这个帖子提出的问题
|
66
justin2018 2019-01-16 22:31:15 +08:00
长时间不写 我总是会忘记~ 不知道为啥~~~
|
67
tomoya92 OP @justin2018 我经常用,都分不清,更别说长时间不用了😂
|
68
libook 2019-01-16 23:42:52 +08:00
如果执意使用回调函数的思想来设计程序的话,用什么语言都会有这个问题。
JS 是可以完全不用回调函数来设计数据流的,你都用 async await 了,为什么还要用回调函数来传递数据?可以拿出几个例子来,一定有更好的代码的组织方案的。 |
69
libook 2019-01-16 23:49:01 +08:00
it ('waitBody', async function() {
const code = await driver.sleep(500).wait('body', 30000).html(); isPageError(code).should.be.false; }) 这块没必要再用 then 了,你都用 await 了,那就一定是等着这段代码执行完再执行下一句。 |
70
lhx2008 2019-01-17 00:02:14 +08:00 via Android
@tomoya92 说 python 快我还信了,普通 js 真的不行,npm 装个包都不知道要多久,一升级就各种瞎改,异步也不好写,异常链+正常链+多线程都难搞死了,维护别人代码都想砍人
|
72
busfool 2019-01-17 06:47:36 +08:00 via Android
没有,js 异步无法优雅处理
|
74
zy445566 2019-01-17 10:05:27 +08:00
把所有的回调都封装成 Promise,然后返回给上面 await 就好了
|
75
KuroNekoFan 2019-01-17 10:31:20 +08:00
关于 promiseFunc.then(some_stuff)的观感问题,你可以
Promise.resolve() .then(stuff_1) .then(stuff_2) //... |
76
salamanderMH 2019-01-17 10:47:10 +08:00
promisify
|
77
no1xsyzy 2019-01-17 11:31:52 +08:00
@oyjw443523 #58 协程不就是 async/await 吗
其实 NodeJS ( Python 的也是)的 async/await 就是把异步变成协程写法。 —— @lhx2008 #70 yarn,请 —— 其实异步除了回调、Promise/Future、async/await (包括协程)还有一种就是信号式。 信号式其实多线程下也能很好用,调用链清晰,程序本身就是个系统设计图。 缺点就是状态不能自然保留和共享(必然手动维护 context ),并且对一本道的代码没特别大的影响。 |
78
exonuclease 2019-01-17 14:36:11 +08:00 1
我搞了个 node 的 c++扩展 可以强制把异步调用同步执行 不过代价是性能
|
79
hoyixi 2019-01-20 13:51:40 +08:00
就你给的代码,你不想写 then 回调,
直接: code = await。。。。 if (判断 code )。。。 这不就是同步写法吗,await 的优势就是能让你这么写啊,你又不用 |
80
angsheng 2019-12-28 23:03:12 +08:00 1
我个人就很喜欢写回调套回调,当发现回调卡死了写不下去时候比如 fs.statu 与 fs.read 的配套操作的时候,问题出在了整个处理流程的设计,反过头重新设计就是了。
当然我理解“回调地狱”,,,但我对 Promise、async/await 就感到特别头疼,总是想不起来这个对象怎么用,真希望能尊重下像我这种习惯写回调而不习惯新语法的。 nodejs 就是 IO 类 API 是与主线程分开的另一个线程(其本身是不是队列结构实在是忘了),所以这里姑且认为至少有两个线程,代码顺序的主线程和 IO 类 API 所在那个线程。Promise 之类折腾来去无非是换了个写法而已(从程序员的层面来看)。 |