大家好,又到了新一期“扯蛋”时间了。
我几乎没怎么用过 callbacks
,promise
,generator
,yield
这些听起来牛逼哄哄的东西。我直接来看 async/await
。有这么多前辈高人,我这小菜就捡了便宜,越过了这一系列发展过程中的酸甜苦辣,直接去偷取“果实”。还有我看CSS的时候,是无视了IE6这个可怕时代,直接看CSS3的知识。这也就是大家所说的速成,没什么内力,没经历过,就不明白现在为啥是这个样子,不扯了,去吃完扯面再说。
我先是看了边城的理解 JavaScript 的 async/await,受益匪浅;接着又找到了allen_he的async/await 执行顺序详解,这篇文章就是我想要的,看下面的评论,不管测试结果,我要的就是这篇的解释和这个思路。我瞅了一眼阮一峰的Node 定时器详解,这和浏览器中的Event loop还是有很多不同之处,尤其每一轮事件循环中要干的活是大大的不同。
此篇仅仅是一个开始,还需投入精力加深理解。
- 让我们测试几个代码,学习研究之用。
1 | console.log("script start..."); |
结果在此,请不要偷看,O(∩_∩)O
script start…
test start…
I’m ss
script end…
r: ss
test end…
how to put a promise to the microtask queue?
next loop, obviously!
- 略微改动。
1 | console.log("script start..."); |
script start…
test start…
I’m ss
script end…
how to put a promise to the microtask queue?
r: ss
test end…
next loop, obviously!
分析一下
这两段代码几乎一毛一样。仅仅是 await 那里,第一次等了个 Function call,第二次等了 AsyncFunction call。说白了,第一次等待返回的不是Promise的值 “ss”;第二次等待返回的是一个Promise的值 #value:”ss”, status:”resolved”#。(这里用词不是很准确,比如 Promise的值,你懂得就好。至于第二个值,只是意思意思表示一下,大概就那样子,没有字面量表示法。)
这个 await 还是很骚的。
第一次,哎呀,是个字符串,不是Promise的值,舍不得交出去,等等吧。跳出了test的执行(注意是跳出去了,暂停了,执行的现场还是保存了)。执行完后面的几句代码,然后搂不住了,await 只好把东西交出来,也就是继续执行。完事,没码可执行,就把 microtask queue 清空了。最后到下一轮task了。
第二次,这货更加过分,因为是个Promise,这货就是不肯交出来。直到 microtask queue 清空了,才知道大势已去,也只能返回这个Promise的值,赶紧执行剩余代码。新的一轮工作又开始了。
简单说就是在 AsyncFunction call 中 await expression, expression的值算出来以后(这里也很讲究,expression的计算,如果放在主线程,还是要卡住,像ajax比较适合,反正不在主线程计算,马上能返回,最后给个结果就行),await 拿到值,现在是不会交出来的;
而且还要对别人说:“滚,离我远点。”,没办法,只能暂时跳出这个 AsyncFunction 了。执行后面的代码。完事后,这里也是一个关键点。如果 await 拿的不是一个 Promise的值,那它就要交出来,AsynFunction call得以继续执行,接着清 microtask queue;但如果 await 手里拿的是一个 Promise的值,不管是pending状态还是完蛋状态,都会先清 mircotask queue,它会死皮赖脸的,要等 Promise 计算完后(非pending状态) ,才肯交出那个值,继续从暂停的地方执行。
当然,这个流程不是 await 控制的,只是把 await 拿到第一人称比较形象。这货就是个拖啊,先拖住再说。 await expression,expression计算出来以后,会有一跳,这就改变了代码的执行流程;待后来,它王者归来,返回胜利的果实。所以,它有自己的适用范围。你确实什么都可以给它 await 一下,但你从这个执行流程也可以看出来,你 await 的任务,如果要在主线程执行,也是没啥卵用。
1 | /* |
看吧,await 后面的先要睡一天,setTimout就几毫秒吧(我机子,console.time('sT');setTimeout(function(){},1000);console.timeEnd('sT');
,cpu G3220,Chrome71 0.05 ~ 0.13 ms, node v10.4.0 0.06 ~ 0.95 ms,交互式不超过0.1ms,文件式就比较耗时,但也没有超过1ms;仅做参考,机器及环境相关),await拿到Promise(是pending状态)。出去算完 Steve_Jobs。 一天后,setTimeout任务来了,可是它又睡了一天,然后 Promise 就resolved了。此时 p 值才得以打印。流程就是这样。或许浏览器会直接让你滚蛋,他么的什么破代码,想卡死我?。nodejs上我也没有试过。
当然,小弟的栗子不好吃,不太恰当。
在下在上篇文章中,也说了,不爱提什么同步异步的。如下片段,
1 | r1 = await job1(); |
异不异步我不管,你口中的异步任务,还是按次序来。重要的是次序。
不过,1
2
3let r = await ajax(some-url);
callback(r);
//...
确实比1
2ajax(some-url, callback);
//...
看起来舒服多了。尤其是你安排很多后续任务,才会有真正的优势。本篇的重点是刻画一下 await 这个货。具体怎么使用,我也不会。拜拜,我们下期再见。
刚才又想了想,有些地方不对
上面吹了那么多牛,后来,我发现,自己不能解释下面的代码,次序都是 step1->step2->step3;可是两段代码的互相穿插,穿插的顺序呢? await 到底和 then 之间有着怎样的爱恨情仇?
1 | function takeLongtime(n) { |
这里我搞不清的是下面的问题:
function cb(n) {}; setTimeout(cb, 5000, 1234);
,当setTimeout执行了,那么可以认为Timer线程在五秒后会把 cb 及其参数 1234 打入冷宫,不,打入事件队列。主线程有空就执行一下。- 那么
new Promise(r=>{ setTimeout(()=>r("呵呵"), 5000) }).then(console.log, console.error);
,它是如何执行的?
- new Promise,首先setTimeout执行了,返回一个 pending 状态的 Promise 对象。(五秒后Timer线程会放
()=>r('呵呵')
进入事件队列;当主线程执行了这个,Promise对象就会resolved,值就是 “呵呵”。) - 我想问的是,new Promise这个构造执行完,返回 pending 状态的 Promise 对象,然后呢?然后干什么?(离开这里,往下执行?将来再回来?)那后面的then也不能不管吧?既然是 pending,那也不能把后面 then 中的两个任务某一个放入 microtask queue 吧?应该是其他线程接管了。这个先放一放。
我又来装逼了
老阮的文章,其中还有朴灵的批注。啥也不说,看了看评论。只是觉得太浮躁了。细思极恐。披着各种外衣的。打着追求真理的幌子。所谓的概念不是自己的理解吗?不断提升吗?就一下子想得到正确的概念,正确的答案?真能一下子得到正确的概念,这个世界就简单了,还要学习吗?或者,哪有什么真理,你真的想过?写操作系统,写v8的那些人,代码还在不断更新,为啥不是一出来就是完美的?哪些可是真的大神啊。在哥德尔手里,数字能推翻公理化;在我手里也只能加加减减卡里剰几毛钱了。什么是“横看成岭侧成峰”,成天背几个概念,顶个毛用。线程咋咋,进程咋咋,你知道线程怎么表示?数据结构有多少字段?(sorry,我不知道)背那么清楚还不如踢足球?
1 | window.isNaN(Object) |
talk is cheap,show me the code。哈哈。