原文发表在: , 本文是汉化重制版。
本系列在 上同步连载。
用ajax胡乱做项目的时候踩过好多坑,然后对JS留下了“非常诡异”的印象。系统学习后,发现这个构建了整个互联网表层的语言其实非常666。这次的学习已经告一段落,本篇也是这个系列的最后一部分。回头看来,把学习记录发出来这个经历挺奇特的,以前是写了给自己看,现在随便搞搞发来掘金就3000+的总阅读,顿时感觉有意义了很多。所以我也想明白了,你看,我就有动力写。
其实没啥新鲜的
阻塞操作的问题
from flask import Flaskimport timeapp = Flask(__name__)@app.route("/lazysvr")def recv(): time.sleep(10) return "ok"if __name__ == "__main__": app.run(host='***.***.***.***', threaded=True)复制代码
复制代码
xmlHttp.send( null ); // it is the aforementioned blocking operation复制代码
ok复制代码
[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience. For more help, check https://xhr.spec.whatwg.org/.复制代码
来一波异步
复制代码
ok复制代码
setTimeout(callback, 3000);function callback() { alert(‘event triggered’);}复制代码
复制代码
新的fetch()接口
在第一个?中,我用回调来举例是因为比较直观。其实更好的办法是用fetch()来进行网络请求。这个函数会返回一个Promise对象,再用这个对象调用then()函数的话:
1. 异步操作的代码就可以变成线性(更像同步)了;
2. 回调地狱的问题可以得到解决了;
3. 所有的相关异常,可以在一个代码块里处理了:
复制代码
运行结果和第一个?一样,我还是留了按钮给你试UI有没有卡。
底层机制,多线程+事件循环
JS不是单线程吗?
答案是,即是也不是。什么意思?
var i;for (i = 0; i < 1000; i++) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open( "GET", "http://***.***.***.***:5000/lazysvr", true ); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { alert(xmlHttp.responseText); } } // end of the callback xmlHttp.send( null );}复制代码
假设浏览器的pid是666(巧了,我做这个测试的时候还真是),我们用一小段脚本(环境是Mac)本来观察线程状态:
#!/bin/bashwhile true; do ps -M 666; sleep 1; done复制代码
初始值(我把无关的列和行都干掉了):
USER PID ... STIME UTIMEholmes 666 ... 0:00.42 0:01.47 ...... 666 0:00.20 0:00.64复制代码
结束的时候:
USER PID ... STIME UTIMEholmes 666 ... 0:00.50 0:01.88 ...... 666 0:00.37 0:01.28复制代码
除了主线程,还有一条非常活跃的线程,我估摸着这条是用来监听网络的(多路复用)套接字。
所以JS代码确实是运行在一条线程里。但是如果从应用程序的角度来看,它其实是多线程。用同样的办法测一下Node吧。
“粗”暴的事件循环
上文提到,操作系统的中断是以指令为粒度的,但是这个传说中的事件循环,粒度就有点大了:
var i;for (i = 0; i < 3; i++) { alert(i);}setTimeout(callback, 0);function callback() { alert(‘event triggered’);}复制代码
我们都知道结果是:
123event triggered复制代码
简单来说呢,虽然我们注册了一个定时事件,并且指定它立即执行,但是JS引擎还是在运行时忠实的把本次循环跑完,才会去理刚刚注册的那个事件。
这个代表一般事件中断是以指令周期为单位,而JS是以循环周期为单位的。
有点尴尬了,这么大粒度的事件处理会不会导致UI响应时间长呢?我觉得其实不会。即使在以指令周期为单位的事件响应里,用户的操作还是需要在本次"循环周期"结束放到主线程来,然后反映到UI。因为一切UI更新都要在主线程。所以,这个极其简化的单线程设计本身并不会对UI性能造成影响。你觉得呢?
迟到的总结
这个系列中,我覆盖了在JS里,。然后我在和里深入到prototype这一层进一步讨论了一下对象。最重要的是,我三次提及了this的坑:
说明真的很重要。
最后就是本篇了,用我理解的角度聊了一下异步。
如果你还记得的话,这个系列是我为新工作(临时)学JS准备的。以现在上手程度来看,我觉得这个底子打的还不错,希望对你也一样。但是这个文章并不全面,所以我准备了如下的附加阅读:
我用来调试的方法
这篇很有趣,我第一次读到,希望有机会能翻译
”
, 最重要的事,
常来掘金看篇。
最后要承认第一段的结构是模仿乔帮主在第一次苹果(iPhone1)发布会的经典段式。(写这篇文章的时候,实在被最新的发布会感动了一把)。如果没看过去找找吧。
感谢阅读,后会有期!