(Day24) 處理非同步的 Promise

前言

在過去 JS 要處理 Ajax 這個非同步語法,會很容易用到 Callback 寫法,而 Callback 不但不好管理,還容易寫成 Callback Hell,如圖:

幸好 ES6 新增了 Promise ,對於 JS 這個單執行序的語言 Promise 非常實用。
Promise 直接翻譯成中文會是承諾 ,而 Promise 的使用結果也就分成兩種

  • 達成承諾,使用 resolve()
  • 承諾失敗,使用 reject()

    實際使用 Promise


    而要使用 Promise 大致上分為兩個步驟:
  • 使用 new Promise() 的函式建構式創造一個新的 Promise 物件
  • 實際執行上面提到的 Promise 物件

    在創造函式建構式時,函式建構式會帶上兩個參數 resolvereject , 第一個參數是用來執行 成功的方法,第二個則是 失敗的方法,這兩個參數名稱接可以自定義,不過實際開發時多數,仍會使用這個名稱做命名。
    (這邊是把 Promise 寫成函式方法,因此可以帶入參數,在透過參數來做判斷。)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function PromiseFn(String) {
    // 創造建構函式並帶上 resolve 、 reject 參數
    return new Promise((resolve, reject) => {
    setTimeout(function () {
    // Promise 承諾的判斷
    if (String.length >= 5) {
    resolve(`Promise 成功, ${ String }${ String.length } 個字 `)
    } else {
    reject('Promise 失敗')
    }
    }, 2000);
    });
    }

    建立好 Promise 函式建構式後,便是執行 Promise 本身了,接者執行 Promise 本身時,我們可以使用 then()catch() 他們會分別接收 Promise 成功 以及 Promise 失敗的結果,但他們需要寫上一個 Callback Function ,若要顯示 Promise 建構式中的 resolve()reject() 設定的資料,那麼會需要在 Callback Function 中帶上一個參數,這些參數就會顯示 resolve()reject() 設定的資料:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    function PromiseFn(String) {
    return new Promise((resolve, reject) => {
    setTimeout(function () {
    if (String.length >= 5) {
    resolve(`Promise 成功, ${ String }${ String.length } 個字 `)
    } else {
    reject('Promise 失敗')
    }
    }, 2000);
    });
    }

    PromiseFn('test')// Promise 失敗

    .then((res)=>{ // 第一個參數會回傳 resolve() 設定資料
    console.log(res)
    })
    .catch((err)=>{
    console.log(err) // 第一個參數會回傳 reject() 設定資料
    })

    PromiseFn('Ryder') //Promise 成功, Ryder 共 5 個字
    .then((res)=>{
    console.log(res)
    })
    .catch((err)=>{
    console.log(err)
    })

    All 與 Race


    扣除上面的 Promise 基本方法,Promise 還提供 Promise.all()Promise.race() 兩種用法。
  • 使用 Promise.all() 時,會執行 Promise.all() 中所有的 Promise 方法,並將回傳一個陣列,而這個陣列就是 resolve() 所提供的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function PromiseFn(String) {
    return new Promise((resolve, reject) => {
    setTimeout(function () {
    if (String.length >= 5) {
    resolve(`Promise 成功, ${ String }${ String.length } 個字 `)
    } else {
    reject('Promise 失敗')
    }
    }, 2000);
    });
    }

    Promise.all([PromiseFn('Ryder'), PromiseFn('youtube')]).then((res)=>{
    console.log(res) //['Promise 成功, Ryder 共 5 個字 ', 'Promise 成功, youtube 共 7 個字 ']
    })
  • Promise.race()Promise.all() 一樣,會同時執行 Promise.race() 中所有 Promise 方法,但他只會回傳最快執行完畢的 Promise 方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const p = new Promise((resolve) => {
    if (true) {
    resolve(`直接執行 Promise`)
    }
    })
    function PromiseFn(String) {
    return new Promise((resolve, reject) => {
    setTimeout(function () {
    if (String.length >= 5) {
    resolve(`Promise 成功, ${String}${String.length} 個字 `)
    } else {
    reject('Promise 失敗')
    }
    }, 2000);
    });
    }

    Promise.race([PromiseFn('Ryder'), p]).then((res) => {
    console.log(res) //直接執行 Promise
    })

    鏈式寫法


    上面介紹的 Promise.all() Promise.race() 都是會同時執行的方法,不過我們肯定會遇到需要依序執行 Promise 的狀況,在過去使用 Callback Function 時就會寫出超巢的 Callback Hell,不過 Promise 則提供了鏈式寫法,可以輕鬆的達成需求。
    要使用 Promise 的鏈式寫法,只需要在 then() 中使用 return 並呼叫下一個 Promise 這樣變能使用 .then() 不斷串聯下去。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    const p = new Promise((resolve) => {
    if (true) {
    resolve(`直接執行 Promise`)
    }
    })

    function PromiseFn(String) {
    return new Promise((resolve, reject) => {
    setTimeout(function () {
    if (String.length >= 5) {
    resolve(`Promise 成功, ${String}${String.length} 個字 `)
    } else {
    reject('Promise 失敗')
    }
    }, 2000);
    });
    }
    PromiseFn('Ryder')
    .then((res) => {
    console.log(res) // Promise 成功, Ryder 共 5 個字
    return PromiseFn('youtube') // Promise 鏈式寫法,可以不斷寫 Promise 下去。
    }).then((res) => {
    console.log(res) // Promise 成功, youtube 共 7 個字
    return p
    }).then((res) => {
    console.log(res) // 直接執行 Promise
    })

    參考文獻

  • JavaScript 核心篇 (六角學院
  • 鐵人賽:使用 Promise 處理非同步