JavaScript
異步編程
一道面試題
for(var i = 0; i < 3; i++) {
setTimeout(function() {
console.log('timeout' + i);
})
}
new Promise(function(resolve) {
console.log('promise1');
for(var i = 0; i < 1000; i++) {
i == 99 && resolve();
}
console.log('promise2');
}).then(function() {
console.log('then1');
})
console.log('global1');
promise1
promise2
global1
then1
3 timeout3
JavaScript
單線程
在瀏覽器的一個頁面中,該頁面的JS程序只有一個線程,故曰單線程。因爲是單線程,所以程序的執行順序就是從上到下依次執行,同一時間內只能有一段代碼被執行。
瀏覽器多進程
- 瀏覽器是多進程的
- 瀏覽器之所以能夠運行,是因爲系統給它的進程分配了資源(cpu、內存)
- 簡單點理解,每打開一個Tab頁,就相當於創建了一個獨立的瀏覽器進程。
-
瀏覽器的渲染進程是多線程的
- GUI渲染線程
- JS引擎線程
- 事件觸發線程
- 定時觸發器線程
- 異步http請求線程
瀏覽器渲染進程圖:
異步機制
for (var i = 0; i < 5; i ++) {
setTimeout(function(){
console.log(i);
}, 0);
}
console.log(i);
//5 ; 5 ; 5 ; 5; 5
回調函數Callback
這是異步編程最基本的方法。
假定有兩個函數f1和f2,後者等待前者的執行結果。
f1();
f2();
如果f1
是一個很耗時的任務,可以考慮改寫f1
,把f2
寫成f1
的回調函數。\
function f1(callback){
setTimeout(function () {
// f1的任務代碼
callback();
}, 1000);
}
執行代碼就變成下面這樣:
f1(f2);
採用這種方式,我們把同步操作變成了異步操作,f1
不會堵塞程序運行,相當於先執行程序的主要邏輯,將耗時的操作推遲執行。
回調函數的優點是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合Coupling
,流程會很混亂,而且每個任務只能指定一個回調函數。
發佈訂閱
發佈-訂閱模式又叫做觀察者模式,他定義了一種一對多的依賴關係,即當一個對象的狀態發生改變的時候,所有依賴他的對象都會得到通知。
let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (fn) {
this.peopleList.push(fn);
}
yourMsg.triger = function () {
for(var i = 0,fn;fn=this.peopleList[i++];){
fn.apply(this,arguments);
}
}
yourMsg.listen(function (name) {
console.log(`${name}收到了你的消息`);
})
yourMsg.listen(function (name) {
console.log('哈哈');
})
yourMsg.triger('張三');
yourMsg.triger('李四');
Promise
Promise
是一個對象,它代表了一個異步操作的最終完成或者失敗。
Promise 最
直接的好處就是鏈式調用(chaining
)
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
生成器Generators/ yield
Generator
function* Hello() {
yield 100
yield (function () {return 200})()
return 300
}
var h = Hello()
console.log(typeof h) // object
console.log(h.next()) // { value: 100, done: false }
console.log(h.next()) // { value: 200, done: false }
console.log(h.next()) // { value: 300, done: true }
console.log(h.next()) // { value: undefined, done: true }
yield
yield
關鍵字使生成器函數執行暫停,yield
關鍵字後面的表達式的值返回給生成器的調用者。它可以被認爲是一個基於生成器的版本的return
關鍵字。
function* countAppleSales () {
var saleList = [3, 7, 5];
for (var i = 0; i < saleList.length; i++) {
yield saleList[i];
}
}
var appleStore = countAppleSales(); // Generator { }
console.log(appleStore.next()); // { value: 3, done: false }
console.log(appleStore.next()); // { value: 7, done: false }
console.log(appleStore.next()); // { value: 5, done: false }
console.log(appleStore.next()); // { value: undefined, done: true }
async/await
async function func() {
try {
let res = await asyncFunc()
} catch (e) {
//......
}
}
async
函數返回的 Promise
對象,必須等到內部所有的 await
命令的 Promise
對象執行完,纔會發生狀態改變
async
函數的語法不難,難在錯誤處理上。
https://juejin.im/post/5a6547...
http://www.ruanyifeng.com/blo...
https://juejin.im/post/5a1681...
https://juejin.im/post/5ce75f...
https://developer.mozilla.org...
https://juejin.im/post/596e14...