iT邦幫忙

2022 iThome 鐵人賽

DAY 4
0
自我挑戰組

菜鳥前端修練之旅系列 第 4

Day 04 | Promise

  • 分享至 

  • xImage
  •  

promise 是 ES6 新增的語法,主要用來處理非同步行為,promise 也可以直接用中文理解:這個承諾可以被兌現或是拒絕,也就對應到了 resolve(成功) 以及 reject(失敗)。

建立 Promise

透過建構函式 new 一波,一旦建立後就有相關的方法可以呼叫(then、catch 等)。

const promise = new Promise((resolve, reject) => {
  resolve("success");
  reject("error");
});

Promise 內必須傳入一個函式,這個函式會接收兩個參數,分別是 resolve & reject(選填),成功及失敗時會被調用,一旦調用後 promise 的狀態就會被改變,同時只能調用其中一種。

Promise 的狀態

上面提到調用後狀態就會被改變,狀態可以分為以下:

  • pending 承諾尚未兌現 (PromiseState:Pending)
  • resolved 承諾已履行 (PromiseState:Fulfilled)
  • rejected 拒絕承諾 (PromiseState:Rejected)

要注意的是,一旦狀態被改變後就不會再更動了。我們可以 log 出 PromiseState 來看看現在的狀態:

const promise = new Promise((resolve, reject) => {
    // ...
});

console.log(promise)

Promise 鏈

一般在使用 promise 會用 thencatch 代表成功及失敗要做的事情:

promise.then();    // Promise 回傳正確
promise.catch();   // Promise 回傳失敗
promise.finally(); // 非同步執行完畢(無論是否正確完成)

then 中使用 return 回傳另一個 promise,就是 Promise 鏈的用法,再下一個 then 中會繼續執行,就不會有 callback hell 的情況發生。

promise
  .then((res) => {
    console.log(res)
    return promise; // 回傳 promise
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });

但如果第二個 promise 被拒絕則會接跳到 catch 的地方。
另外 finally() 非同步執行完畢後才會執行,可以用來確認該 promise 是否有執行。

Promise API

接下來看看 Promise 還有哪些方法可以使用:

Promise.all(iterable)

當我們想要一次執行多個 promise 就可以使用 all(),參數中接受陣列,陣列中放多個 promise

用法與一般的類似,實際寫會類似這樣:

// n < 3 才會成立
function promise(n) {
  return new Promise((resolve, reject) => {
    if (n < 3) { 
      resolve("success");
    } else {
      reject("error");
    }
  });
}

// 參數放陣列,這邊都成功所以不會進到 catch
Promise.all([promise(1), promise(2)])
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });

all() 的注意點:

  • 所有 promise 完成後會回傳一個陣列,陣列裝著各個 promisecallback
  • 只要其中一個變成 rejected,則回傳第一個 rejectedpromise

Promise.race(iterable)

與上面的 all() 差不多,參數一樣接受陣列,差別在於:

只要任一個 promise 狀態改變,則回傳該個 promisecallback

Promise.resolve(value)

Promise.resolve() 函數用來將一個物件轉型為 Promise (如果它不是一個 Promise 物件),然後立刻 resolve 它。

const promise = Promise.resolve("success");
promise.then((res) => {
  console.log(res); // success
});

Promise.reject(reason)

Promise.reject() 函數用來將一個物件轉型為 Promise (如果它不是一個 Promise 物件),然後立刻 reject 它。

簡單來說就是跟上面相反

const promise = Promise.reject("error");
promise.catch((err) => {
  console.log(err); // error
});

Async / Await

Async Await 是 ES7 新增的語法,也同樣用來處理非同步行為、並且是 Pormise 的語法糖,能讓函式以「同步」的方式來執行程式。

語法上有兩個重點:

  • function 前面添加 async 關鍵字。
  • 通常使用 try & catch 做例外處理。
// 建立一個 promise
const promise = new Promise((resolve, reject) => {
    resolve(1)
});

// 立即執行這個 async function
(async function asyncFn(){
    try {
        const res = await promise;
        console.log(res) // 印出 1
    }catch(err){
        console.log(err)
    }
})()

await

上面的範例中可以看到 await,這個用法等同於 promise 中使用的 then。這個 API 與 async 是好朋友,並不會單獨出現,也就是外層如果沒有 async 關鍵字,內部無法使用 await

當然 await 沒有規定執行次數:

const promise = (number) => {
    return new Promise((resolve, reject) => {
        resolve(number)
    })
}

(async function asyncFn(){
    try {
        const res = await promise(1);
        const res2 = await promise(2);
    }catch(err){
        console.log(err)
    }
})()

try & catch

Promise 中使用 catch 來捕捉錯誤,在 async function 如果沒有特別針對錯誤做處理的話,一旦發生錯誤會導致程式碼無法繼續執行。

// 參數 number 大於 10,進入 reject
const promise = (number) => {
    return new Promise((resolve, reject) => {
        if(number > 10){
            reject("WTF!!!!")
        }else{
            resolve(number)
        }
    })
}

(async function asyncFn(){
    try {
        // 進入 rejected 狀態,前往 catch
        const res = await promise(99); 
    }catch(err){
        console.log(err) // 印出 WTF!!!!
    }
})()

參考資料


上一篇
Day 03 | CDN
下一篇
Day 05 | 同源政策(Same Origin Policy)
系列文
菜鳥前端修練之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言