iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 6
1
Modern Web

你應該要知道的新一代Web技術---漸進式網頁(PWA)系列 第 6

[Day06] 複習一下Jacvascript中的 Promise 和 Fetch(Part1)

在Service Woker中會用到大量的promise和Fetch API,所以這邊先來好好複習一下

JavaScript程式語言在設計時,需考量非同步、單一執行緒等等的問題

JavaScript程式執行時的確都在單一個執行緒(Single Thread)中執行的,聽起來有點不可思議吧! /images/emoticon/emoticon36.gif
現在的電腦硬體不都是多核心(多執行緒)嗎?這種設計對於程式執行不會有問題嗎?其實JavaScript從一開始就是這樣設計,它執行的主要環境是在browser上,只有一個使用者,而且是在資源受限的環境中執行,這是一個很合理的設計。

JavaScript程式執行雖然是Single Thread,但server或browser執行環境並不是:表面上看起來是只有一個執行緒在執行JavaScript程式,但實際上在背後有數個在其他執行環境中的執行緒,在輔助程式碼的執行。

外部資料的執行時間,大部份都是等待時間(像連接資料庫、執行資料庫查詢等等),真正執行是在資料庫裡,程式只是傳送對應的query而已,並不是在JavaScript程式中執行查詢,這種情況對JavaScript程式而言,大部份的執行時間中都是在等待查詢結果而已。讀寫檔案、網路連線要求與回應、傳送資料等等,都有類似的情況。


要理解JavaScript是如何執行程式,首先要先理解同步(Synchronous, sync)與非同步(Asynchronous, async)程式執行的差異:

Synchronous code是指程式碼的執行順序,都是由上往下依順序執行,一個執行程序完成後才會再接著下一個,一般的程式語言都是按照這樣的流程來執行,JavaScript語言也不例外。

例如像連接資料庫存取資料的程式,應該會遵守下列的步驟進行:

  1. 連接資料庫(給定帳號、密碼、主機、資料庫名)
  2. 執行資料庫查詢語法
  3. 取得資料

這對於「從資料庫查詢資料」的這種程式本身並沒有太特別的地方,一般都是這樣執行沒錯。但對於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函式,他們分別代表:

  • 「兌現 (fulfilled)」通常以resolve命名該參數,當我們完成動作時,就呼叫resolve()來兌現我們的承諾。(見上圖第一個範例)
  • 「拒絕 (rejected)」通常以reject命名該參數,當我們動作失敗時,就呼叫reject()來打破我們的承諾。(見上圖第二個範例)

另外promise的強大之處在於可以利用then()來串接不同的asynchronous code,來避免callback hell

在我的PWA中哪裡有使用到Promise哩?

在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 結束!! /images/emoticon/emoticon25.gif


上一篇
[Day05] Service Worker の 基礎介紹(Part2)
下一篇
[Day07] 複習一下Jacvascript中的 Promise 和 Fetch(Part2)
系列文
你應該要知道的新一代Web技術---漸進式網頁(PWA)29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言