在Service Woker中會用到大量的promise和Fetch API,所以這邊先來好好複習一下
JavaScript程式語言在設計時,需考量非同步、單一執行緒等等的問題
JavaScript程式執行時的確都在單一個執行緒(Single Thread)中執行的,聽起來有點不可思議吧!
現在的電腦硬體不都是多核心(多執行緒)嗎?這種設計對於程式執行不會有問題嗎?其實JavaScript從一開始就是這樣設計,它執行的主要環境是在browser上,只有一個使用者,而且是在資源受限的環境中執行,這是一個很合理的設計。
JavaScript程式執行雖然是Single Thread,但server或browser執行環境並不是:表面上看起來是只有一個執行緒在執行JavaScript程式,但實際上在背後有數個在其他執行環境中的執行緒,在輔助程式碼的執行。
外部資料的執行時間,大部份都是等待時間(像連接資料庫、執行資料庫查詢等等),真正執行是在資料庫裡,程式只是傳送對應的query而已,並不是在JavaScript程式中執行查詢,這種情況對JavaScript程式而言,大部份的執行時間中都是在等待查詢結果而已。讀寫檔案、網路連線要求與回應、傳送資料等等,都有類似的情況。
要理解JavaScript是如何執行程式,首先要先理解同步(Synchronous, sync)與非同步(Asynchronous, async)程式執行的差異:
Synchronous code是指程式碼的執行順序,都是由上往下依順序執行,一個執行程序完成後才會再接著下一個,一般的程式語言都是按照這樣的流程來執行,JavaScript語言也不例外。
例如像連接資料庫存取資料的程式,應該會遵守下列的步驟進行:
這對於「從資料庫查詢資料」的這種程式本身並沒有太特別的地方,一般都是這樣執行沒錯。但對於JavaScript這種只有Single Thread的程式語言,這樣作會造成阻塞(blocking),也就是說當這個資料庫查詢的執行程序,需要很長的一段時間才能結束時,在這期間其他的操作都會停擺,像是滑鼠要點按按鈕之類的功能,就完全沒有作用。因此,我們需要用另一種不同的方式,來進行這類會阻塞其他程式的執行,也就是非同步程式執行的方式。
Asynchronous code執行的作法,是使用非同步callback(回調)函式的語法,讓會造成阻塞的程式組成一個非同步回調函式,先丟往一個任務佇列(task queue),在之後的某個時間再回傳它的值與狀態回來。這些函式裡的程式碼多半都是與外部資源存取的I/O有關,如果需要等待的話,是在實作的API裡(外部模組),並不是在佇列或語言執行緒中,等到超時或有回應後再加入到佇列中。事件迴圈在主執行緒完全沒有其他的EC時,再加回到主執行緒中執行。
最常見的就是setTimeout function,它會在某個設定的時間的執行其中的callback(回調)函式傳入值一次:
但是使用callback很容易會造成所謂的callback hell
所以在ES6語法中新增了一個「Promise(承諾)語法」來取代原始的callback function。(舊版本的browser或許不支援)
這裡我直接將上面的範例改寫成promise的形式來進行說明:
console.log('Hi');
var promise = new Promise(function(resolve, reject) {
setTimeout(function cb() {
resolve('There');
}, 2000);
});
promise.then(function(result) {
console.log(result);
})
console.log('Everybody');
console.log('Hi');
var promise = new Promise(function(resolve, reject) {
setTimeout(function cb() {
reject({code: 500, message: 'An error occur'});
}, 2000);
});
promise.then(function(result) {
console.log(result);
}).catch(function(err) {
console.log(err);
});
console.log('Everybody');
Promise object可以帶入一個函式,代表著今天你要給予承諾的程式。這個函式會被傳入兩個參數,這兩個參數也是callback函式,他們分別代表:
另外promise的強大之處在於可以利用then()來串接不同的asynchronous code,來避免callback hell
在app.js中我們註冊service worker時,呼叫了register這個method。它回傳回來的就是一個promise object,之後我們就可以用then()來handle這個promise的resolve結果,又或者用catch()來處理這個promise被reject的結果。(當然這裡我只印出結果而已)
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function() {
console.log('Service worker registered!');
}).catch(function(err) {
console.log(err);
});
}
另外,在sw.js裡當我們在監聽fetch event時,使用event.respondWith()來攔截我們對外回傳來的結果,這是一個非同步的函式。
還有像是fetch API也是,它允許我們透過HTTP Request來拿到我們要的資料並回傳一個promise object,這我就明天再來說明好了XD
Day06 結束!!