iT邦幫忙

1

[穩扎穩打系列] - [Javascript] 什麼你說不太懂 Promise ,沒關係我教你

  • 分享至 

  • xImage
  •  

前言

不知道你有沒有注意過早上的起床順序呢,像是作者本來起床的順序是:

  1. 拉開被子
  2. 刷牙
  3. 喝咖啡
  4. 上班

有注意到嗎這些事情都是一個一個執行,相信應該沒有人可以一邊刷牙一邊喝咖啡吧!
人腦是如此,電腦也不意外同時間一個執行緒能做的也就是一件事情,

JS 之中要做到有順序的處理 Function 其中有幾個方式 callback function、Promise、 為什麼會發生 callback Hell,在本章節都會提到!

廢話不多說我們一起往下走吧!


Callback Function

我們先來介紹不可不知道的 Callback Function。

當我們在一般撰寫 Funciton 時,呼叫三件事情會是這樣呼叫的。

const A = function () {}
const B = function () {}
const C = function () {}

A()
B()
C()

可以看到執行順序是 A() -> B() -> C()。
原因是這三個 Function 都是立即執行所以會按照順序完成。

但如果這三件事情有一個 Function 出現了延遲會發生什麼事情呢?

const A = function () { setTimeOut(()=>{},1000)}
const B = function () {}
const C = function () {}

A()
B()
C()

執行順序變成 B() -> C() -> A()。

那如果我們想要讓執行順序變 C()-> A() -> B() ,那 Callback Function 會是您的好選擇。

Callback Function 主要帶來的效益是 可以控制執行順序的程式
而什麼是 Callback Function 呢

Callback: 將一 Function 透過參數的方式傳進另一 Function 之中。
Callback Function: 被當成參數傳遞的 Function 稱作 Callback Function。

讓可以直接看到例子。

/**
* params {object} Breakfask
* params {function} TakeBus
*/
const eatBreakfast = (breakfast, takeBus) => {
    //- 萬惡小黃瓜不吃了不上班了!
    if(小黃瓜 in breakfast) return
    
    takeBus()
}

//- takeBus 就是 Callback Function

在了解 Callback Function 之後,我們可以改寫一下上面的例子,我們預期執行順序: C()-> A() -> B() 。

const A = function (func) { setTimeOut(()=>{func()},1000)}
const B = function () {}
const C = function () {}

A(B())
C()

這樣就可以做到你想的事情摟!! 這樣的作法經常被應用於 Ajax,因為 Ajax 是向外部進行請求時需等待資訊,並判斷下一步應該透過哪一個 Function 來進行處理。

但如果今天 CallBack Function 非常的多層就會看到這樣哩。

function helloIsme() {
  console.log("Hello is me");
}

doSomething(result => {
  doSomething(result, (_r) => {
    doSomething(_r, (_fr) => {
        console.log("Here's Johnny")
    }, helloIsme);
  }, helloIsme);
}, helloIsme);

Johnny 就會出現(誤) Here's Johnny

沒有拉 Johnny 不會出現只會有維護這段程式碼的人會出現,當層數變多還可以順便打波動拳呢,終於可以用上這張圖片了。

波動拳

這樣的多層結構被稱作於 Callback Hell,這樣就讓維運/接手人員折壽,所以 Promise 這東西出來拯救世人了。


Promise

Promise 物件代表一個即將完成、或失敗的非同步操作,以及它所產生的值。 引用自Promise - JavaScript | MDN

換句話說 Promise 是一個非同步程序,當程序完成並成功時可以接應成功資料,反之失敗時可以接應失敗的物件。

那上面提到的 callback function 產生的問題 callback hell 就可透過狀態進行反饋。

doSomething((result) => {
  doSomething(result, (_r) => {
    doSomething(_r, (_fr) => {
        console.log("Here's Johnny")
    }, helloIsme);
  }, helloIsme);
}, helloIsme);

//- 就會變成
doSomething()
  .then(()=>{reutrn doSomething()})
  .then(()=>{return doSomething()})
  .then(()=>{ console.log("Here's Johnny") })
  .catch(()=>{})

是不是變的相當簡潔呢。

在學習前我們來了解一下 Promise 物件,此物件在執行中會有以下幾種狀態:

擱置(pending):初始狀態,不是 fulfilled 與 rejected。
實現(fulfilled):表示操作成功地完成。
拒絕(rejected):表示操作失敗了。

在 Promise 結果為 fulfilled 時代表 Promise resolve,透過 .then() 的方式可以編寫成功該做何事。
在 Promise 結果為 rejected 時代表 Promise reject,透過 .catch() 的方式可以編寫失敗該做何事。

使用方式

Promise 是建構函式,需要透過 new Promise() 來進行建置,除此之外還需要給予 Function ,告知 Promise 需如何進行成功與失敗的判斷,function 內容必須有兩個參數 resolve, reject ,分別代表成功與失敗,可以看見下方例子。

const _promise = new Promise((resolve,reject)=>{
//- todo something 成功
resolve()
//- todo something 失敗
reject()
})

* resolve 為成功取得的資料時,回傳資料。
* reject 則會在失敗時回傳 error object。

使用上會有幾個 function 來表達此 Promise 狀態。

  • .then() : 接獲 resolve 結果時會執行。
  • .catch() : 接獲 reject 結果時會執行。
  • .finally() : 當此 Promise 所有 .then / .catch 完成後執行。
_promise
  .then((res)=>{console.log('todo success thing')})
  .catch((err)=>{console.log('todo error thing')})
  .finally(()=>{console.log('todo finally thing')})

如果文字上看得不太清楚的話可以直接看下圖。

流程圖

再來我想要特別提到 Promise 中很重要的概念,鏈結(Promise Chain),如果想要使用鏈結的話需要在 then 中進行 return 一個 Promise,而下一個的 then 就會是 return 的結果。

Chain

需要特別注意: Promise 可以有很多的 then ,但是只能有一個 catch 。


Promise.resolve,Promise.reject

透過此方法來建置 Promise 的話,此 Promise 結果已經決定。
Promise.resolve 此結果必定成功。
Promise.reject 此結果必定失敗。

const _promise = new Promise((resolve,reject)=>{
    resolve('success')
    reject('error')
})
//- 只會成功不會失敗 'success'
_promise.resolve((res)=>{ console.log(res.data) })
//- 只會失敗不會成功 'error'
_promise.reject((err)=>{ console.log(err) })
Promise.all

此方法用於多個 Promise 一同執行,並且等待所有 Promise 後才會一同返回,注意當有一個 Promise reject ,就會進入 catch ,必須全部回傳成功(resolve)才會進入 then()。。

const A = new Promise((resolve,rejcet)=>{resolve(1)})
const B = new Promise((resolve,rejcet)=>{resolve(2)})
const C = new Promise((resolve,rejcet)=>{resolve(3)})
const D = new Promise((resolve,rejcet)=>{reject('error')})

Promise.all([A,B,C])
.then(([resA,resB,resC])=>{
  console.log(resA,resB,resC)
})
.catch(error=>{console.log(error)})
//- console: 1, 2, 3

Promise.all([A,B,D])
.then(([resA,resB,resD])=>{
  console.log(resA,resB,resD)
})
.catch(err=>{
  console.log(error)
})
//- 'error'

也因為有一個 reject 就會進入 catch,所以會沒有辦法知道是哪一個 Promise 錯誤導致無法偵錯。

Promise.allSettled

雖與 Promise.all 一樣是,多個 Promise 進行同時處理,並等待完成後一起回傳。
每一個回傳都會包含 {status,value,reason}

  • status 代表此 Promise 是否成功 fulfilled / rejected
  • value 當 Promise 成功時,此數值為 resolve 夾帶資訊
  • reason 當 Promise 失敗時,此數值為 reject 夾帶資訊
const A = new Promise((resolve,rejcet)=>{resolve(1)})
const B = new Promise((resolve,rejcet)=>{resolve(2)})
const C = new Promise((resolve,rejcet)=>{resolve(3)})
const D = new Promise((resolve,rejcet)=>{reject('error')})

Promise.all([A,B,C,D])
.then((res)=>{
  console.log(res)
})
.catch(error=>{console.log(error)})
//- console: { status: 'fulfilled', value: '1' }, { status: 'fulfilled', value: '2' }, { status: 'fulfilled', value: '3' },{ status: 'rejected', reason: 'error' }
常見疑問

Q1. Ajax 與 Promise 的差異?
Ans: Ajax 是處理 Client 與 Server 之間的溝通方式; Promise 是處理非同步的語法。舉個例子來比喻 Ajax 是人與人的溝通技巧,Promise 則是人溝通所說的語言。

引用

MDN-Promise


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言