原文發表在: 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)發佈會的經典段式。(寫這篇文章的時候,實在被最新的發佈會感動了一把)。若是沒看過去找找吧。
感謝閱讀,後會有期!