iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
0
自我挑戰組

菜雞們,讓我們一起征服JS及React吧系列 第 22

React菜雞-Day22:學會JS獨特的用法,讓你的React更優雅~ 給個承諾Promise

  • 分享至 

  • xImage
  •  
tags: 鐵人賽 React javascript nodejs

鐵人賽第22天,今天我們要談談JSPromise。開發前端不免要對後台要資料,Promise的觀念能幫助我們理解。/images/emoticon/emoticon30.gif

聊聊承諾Promise這檔事

  • 人跟人之間的互動,常常會用承諾Promise來確認彼此的關係。
女友說~「鼻鼻~今晚八點吃飯,餐廳等,別忘囉~!」
面對喜歡的女生,你信誓旦旦給了一個承諾~「當然~因為妳值得~」
承諾之後,腦子會自動模擬兩種狀況...
(1) 準時赴約 => 感情加溫   (resolve,承諾達成)
(2) 干~忘了 => 女友爆炸了 (reject, 拒絕履行承諾)//<-- 母湯~很危險

異步程式

  • 一般我們寫的程式都是循序執行,異步程式是什麼?其實你一定用過,例如setTimeout就是一個異步設計
  • 例如:
  • 老闆說, 十分鐘之後回我電話,你答應後,不會傻傻的等,你會滑滑手機,關心一下喜歡的女生~安安~你好~睡了沒~?
  • 時間到了,電話撥出
  • 簡單來說,異步程式其實說穿了,就是平行設計的概念,主執行緒在運作的同時,另一個任務也同時被完成。

  • JS雖然是單執行緒,不過,透過運行於Node瀏覽器提供的API協助,這些異步程式可以在背景輕鬆完成!

  • Promise就是提供我們異步設計,例如,前端的網頁需要先確認後台的連線是否正常,你會有以下的設計:

(1). 啟動異步:檢察網路連線 
(2). 等待網頁的同時,先處理其他的畫面,或顯示loading...
(3). (1)回覆連線狀態,成功! 開始顯示完整網頁
                   失敗! 出現GG的畫面

Promise...then的用法

  • 我們改寫上張圖來說明

  • Promise函式有兩個輸入的函式變數:resolve及reject,在這裡,你可以做些事情,並判斷事情的結果符不符合你的預期。

  • 如果你的程式「符合」你的預期,你可以用resolve去觸發resolved事件
  • 如果你的程式「不符合」你的預期,你可以用reject去觸發resjected事件
  • thenpromise裡的一個函式,同樣吃兩個函式變數,負責處理resolvereject事件。

準備

來個栗子

  • 假設我們的前端程式運行前,我們希望能用異步的方式,先呼叫checkBackend函式,我們先假設都會回傳true
  • 因為回傳true,我們用resolve函式來做個回應,此時Promise的狀態從pending -> resolved
  • => 程式的第10行,用(*)做記號
  • 接著,我們的Promise變數,透過then觸發了resolve事件,進而處理。
  • => 程式的第16行開始,用(*)做記號
function checkBackend(){
  // do something to check
  return true; //<-- 感謝Dennis Zheng提醒, false修正為true
}

let p = new Promise((resolve, reject)=>{
  let status = checkBackend()  //<-- 回傳值
  
  if(status)
    resolve("Backend is ready!!") //true, 執行resolve(*)
  else
    reject("oops...something's wrong~~>_<"); //false, 執行reject
  
})

p.then(resolveMsg=>{ // 處理resolve事件(*)
  console.log(resolveMsg);
  },
  rejectMsg=>{// 處理reject事件
  console.log(rejectMsg)
})

//=== 終端機會印出如下訊息 ===
//Promise { <pending> }
//> Backend is ready!!
  • checkBackend()的回傳值改為false看看
function checkBackend(){
  // do something to check
  return false; 
}

...
p.then(resolveMsg=>{ // 處理resolve事件
  console.log(resolveMsg);
  },
  rejectMsg=>{// 處理reject事件 (*)<--換reject觸發
  console.log(rejectMsg)
})

//=== 終端機會印出如下訊息 ===
//Promise { <pending> }
//> oops...something's wrong~~>_<
...

then裡面只能有一個resolve或者reject

  • 你只能有一個判斷的決定,以下面這個例子來說,我在Promise裡的func依序呼叫resolvereject
  • 但最終也只會處理第一個,也就是resolve,所以在第6行會印出okla
let p = new Promise((resolve, reject)=>{
  resolve("okla"); //<--只有第一個會被觸發,
  reject("oops");  //<--空炮彈,Promise不會處理
})

// 感謝Dennis Zheng提醒, console.log(resolve) 修正為 console.log(resolveMsg)
p.then((resolveMsg)=>console.log(resolveMsg), (rejectMsg)=>console.log(rejectMsg)) //okla

Promise...then...catch

  • 如果你想讓程式碼更乾淨一點,另一種可以更優雅的寫法,使用catch來處理reject

  • 我們改寫一下上面的範例

  p.then(
    resolveMsg=>{ // 處理resolve事件
    console.log(resolveMsg);
  }).catch(
    rejectMsg=>{// 處理reject事件 (*)<--換reject觸發
    console.log(rejectMsg)
  })
  • 如何,層層分明,簡潔就是帥!

Finally

  • 承諾的事,總有個結局。finally,就是我們最後要處理的部分,你可以在這個在finally進行最後的步驟,例如:提醒主程式狀態為done,清理記憶體...等

  • 注意finally雖然吃一個函式,但要注意,函式沒有任何的變數進來喔!

  • 一樣,我們來改寫上面的例子

  p.then(
    resolveMsg=>{ // 處理resolve事件
    console.log(resolveMsg);
  })
  .catch(
    rejectMsg=>{// 處理reject事件 (*)<--換reject觸發
    console.log(rejectMsg)
  })
  .finally(()=>{
    // 清理記憶體 或者 通知系統你已經完成任務了
    console.log("Done"); 
  })
  • 下圖就是印出的結果,finally在最後被執行了!

最後,我想聊聊Promise真正的觸發點

  • 我們使用setTimeoutsetInterval來當作我們驗證的程式
let p = new Promise((resolve, reject)=>{  //<--(*) Promise宣告後即正式啟動
  let counter = 0; 
  let timer = setInterval(()=>console.log(counter++), 1000);  // 每一秒counter+1, 並印出
  setTimeout(
    ()=>{
      clearInterval(timer);
      resolve("OKLA");
    }, 5000);  // 5秒的時候,發出OKLA
    
})

p.then(
  (resolve)=>console.log("done~"+resolve)
)
  • 你會發現,當Promise一宣告完之後,就馬上啟動了,也就是程式碼第一行的位置(*)

結論

  • 以我們最上面的第一個栗子來看,流程圖如下,實線是實際跑的流程,虛線則是沒有跑的,整個圖就是Promise的完整架構。
  • 第一次接觸Promise的人可能需要點時間消化,慢慢練習,會駕輕就熟的!

補充教學資源

/images/emoticon/emoticon62.gif


上一篇
React菜雞-Day21:學會JS獨特的用法,讓你的React更優雅~ 閉包Closure
下一篇
React菜雞-Day23:學會JS獨特的用法,讓你的React更優雅~給個一連串的承諾~Promise Chain
系列文
菜雞們,讓我們一起征服JS及React吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
yanzhong
iT邦新手 4 級 ‧ 2021-01-11 15:35:10

(2) 干~忘了 => 女友爆炸了 (reject, 拒絕履行承諾)//<-- 母湯~很危險
真的Hen危險哈哈

大大妳兩個範例都是return false喔 console.log的部分寫錯了

小偉哥 iT邦新手 4 級 ‧ 2021-01-17 14:35:13 檢舉

感謝Dennis Zheng大大的提醒,已更正兩個錯誤的地方,並在程式碼的地方,標示大大的名稱,以示感謝!

yanzhong iT邦新手 4 級 ‧ 2021-01-17 18:44:50 檢舉

大大不用這樣啦
因為有你的文章才讓我學到相關知識 我該感謝你才對!/images/emoticon/emoticon08.gif

我要留言

立即登入留言