原文发表在: holmeshe.me , 本文是汉化重制版。javascript
本系列在 Medium上同步连载。html
用ajax胡乱作项目的时候踩过好多坑,而后对JS留下了“很是诡异”的印象。系统学习后,发现这个构建了整个互联网表层的语言其实很是666。此次的学习已经告一段落,本篇也是这个系列的最后一部分。回头看来,把学习记录发出来这个经历挺奇特的,之前是写了给本身看,如今随便搞搞发来掘金就3000+的总阅读,顿时感受有意义了不少。因此我也想明白了,你看,我就有动力写。java
from flask import Flask
import time
app = Flask(__name__)
@app.route("/lazysvr")
def recv():
time.sleep(10)
return "ok"
if __name__ == "__main__":
app.run(host='***.***.***.***', threaded=True)复制代码
<html>
<head>
</head>
<body>
<button type="button">Click Me!</button>
<script>
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "http://***.***.***.***:5000/lazysvr", false ); // false for synchronous request
xmlHttp.send( null ); // the thread is suspended here
alert(xmlHttp.responseText);
</script>
</body>
</html>复制代码
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/.复制代码
<html>
<head>
</head>
<body>
<button type="button">Click Me!</button>
<script>
var xmlHttp = new XMLHttpRequest();
-- xmlHttp.open( "GET", "http://192.241.212.230:5000/lazysvr", false );
++ xmlHttp.open( "GET", "http://192.241.212.230:5000/lazysvr", true ); // 1) change the param to "true" for asynchronous request
++ xmlHttp.onreadystatechange = function() { // 2) add the callback
++ if(xmlHttp.readyState == 4 && xmlHttp.status == 200) {
++ alert(xmlHttp.responseText);
++ }
}
xmlHttp.send();
-- alert(xmlHttp.responseText);
</script>
</body>
</html>复制代码
ok复制代码
setTimeout(callback, 3000);
function callback() {
alert(‘event triggered’);
}复制代码
<html>
<head>
</head>
<body>
<button type=”button” onclick=”callback()”>Click Me!</button>
<script>
function callback() {
alert(‘event triggered’);
}
</script>
</body>
</html>复制代码
在第一个🌰中,我用回调来举例是由于比较直观。其实更好的办法是用fetch()来进行网络请求。这个函数会返回一个Promise对象,再用这个对象调用then()函数的话:python
1. 异步操做的代码就能够变成线性(更像同步)了;web
2. 回调地狱的问题能够获得解决了;ajax
3. 全部的相关异常,能够在一个代码块里处理了:chrome
<html>
<head>
</head>
<body>
<button type=”button” onclick=”callback()”>Click Me!</button>
<script>
fetch(‘http://***.***.***.***:5000/lazysvr') .then((response) => { return response.text(); }).then((text) => { alert(text); }).catch(function(error) { console.log(‘error: ‘ + error.message); }); </script> </body> </html>复制代码
运行结果和第一个🌰同样,我仍是留了按钮给你试UI有没有卡。flask
答案是,便是也不是。什么意思?vim
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/bash
while true; do ps -M 666; sleep 1; done复制代码
初始值(我把无关的列和行都干掉了):
USER PID ... STIME UTIME
holmes 666 ... 0:00.42 0:01.47
......
666 0:00.20 0:00.64复制代码
结束的时候:
USER PID ... STIME UTIME
holmes 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’);
}复制代码
咱们都知道结果是:
1
2
3
event triggered复制代码
简单来讲呢,虽然咱们注册了一个定时事件,而且指定它当即执行,可是JS引擎仍是在运行时忠实的把本次循环跑完,才会去理刚刚注册的那个事件。
这个表明通常事件中断是以指令周期为单位,而JS是以循环周期为单位的。
有点尴尬了,这么大粒度的事件处理会不会致使UI响应时间长呢?我以为其实不会。即便在以指令周期为单位的事件响应里,用户的操做仍是须要在本次"循环周期"结束放到主线程来,而后反映到UI。由于一切UI更新都要在主线程。因此,这个极其简化的单线程设计自己并不会对UI性能形成影响。你以为呢?
这个系列中,我覆盖了在JS里被细化的 "等于" 操做符和 "null" 值,被简化的 字符串,数组,对象和字典。而后我在这篇和这篇里深刻到prototype这一层进一步讨论了一下对象。最重要的是,我三次说起了this的坑:
说明真的很重要。
最后就是本篇了,用我理解的角度聊了一下异步。
若是你还记得的话,这个系列是我为新工做(临时)学JS准备的。以如今上手程度来看,我以为这个底子打的还不错,但愿对你也同样。可是这个文章并不全面,因此我准备了以下的附加阅读:
The debug technique 我用来调试的方法
这篇颇有趣,我第一次读到,但愿有机会能翻译 interesting topic
Another place to understand “this”
A good blog, 最重要的事,
常来掘金看篇。
最后要认可第一段的结构是模仿乔帮主在第一次苹果(iPhone1)发布会的经典段式。(写这篇文章的时候,实在被最新的发布会感动了一把)。若是没看过去找找吧。
感谢阅读,后会有期!