Promise

簡介

  • 主要用於非同步操作,是一個由 asynchronous function return 的物件
  • 用來表示一個異步操作的最终完成(或失敗)及其结果值
  • 代理一個建立時不用預先得知結果的值
  • 解決callback hell的問題......我的超人

語法

  • 只能使用new關鍵字創建
new Promise(executor)

Promise 構造函式包含兩個參數:

  1. resolve
  • 將 promise 的狀態從 pending 更改為 fulfilled(成功)
  • 將非同步函式執行後的結果作為參數傳送給.then( )
  1. reject
  • 將 promise 的狀態從 pending 更改為 rejected (失敗)
  • 將錯誤訊息作為參數傳送給.catch( )

Promise 狀態

  • pending:事件運行中,尚未取得結果(初始狀態)
  • resolved:事件已經執行完畢且成功操作,回傳 resolve 的結果(該承諾已經被實現 fulfilled)
  • rejected:事件已經執行完畢但操作失敗,回傳 rejected 的結果
promise()
  .then((success) => {
    console.log(success)
  })
  .catch((fail) => {
    console.log(fail)
  })

範例:創建一個 promise

const getUser = new Promise((resolve, reject) => {
  // Do some async task
  setTimeout(() => {
    let error = false
    if (!error) {
      resolve({ name: 'Blur', age: 40 })
    } else {
      reject('Error: Something went wrong...')
    }
  }, 1000)
})

getUser
  .then((user) => console.log(user))
  .catch((error) => console.log(error))
  .finally(() => console.log('The promise has been resolved or rejected'))

🤔 創建 Promise 構造函式,executor內的程式碼是會立即執行的

範例 1

console.log('開始')

const myPromise = new Promise((resolve, reject) => {
  console.log('Promise內部 (同步執行)')
  resolve()
})

console.log('結束')

// output
// 開始
// Promise內部 (同步執行)
// 結束

範例 2

// This is a JavaScript Quiz from BFE.dev

console.log(1)
const promise = new Promise((resolve) => {
  console.log(2)
  resolve()
  console.log(3)
})

console.log(4)

promise
  .then(() => {
    console.log(5)
  })
  .then(() => {
    console.log(6)
  })

console.log(7)

setTimeout(() => {
  console.log(8)
}, 10)

setTimeout(() => {
  console.log(9)
}, 0)

// 出現順序:1 2 3 4 7 5 6 9 8

🤔 如果有兩個resolve

  • 一開始調用了resolve(1),Promise 的狀態從Pending被修改成fulfilled
  • 後續的resolvereject都會被忽略
new Promise((resolve, reject) => {
  resolve(1)
  resolve(2)
  reject('error')
}).then(
  (value) => {
    console.log(value)
  },
  (error) => {
    console.log('error')
  }
)

// output
// 1

範例:決定非同步函式的執行順序

  • 希望執行順序: fun1 => fun2 => fun3
// 使用Promise
function fun1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('hello')
    }, 3000)
  })
}

function fun2(data) {
  return new Promise((resolve, rejcet) => {
    setTimeout(() => {
      resolve(data)
    }, 2000)
  })
}

function fun3(data) {
  return new Promise((resolve, reject) => {
    console.log(data)
  })
}

fun1()
  .then(fun2)
  .then(fun3)
  .catch((error) => {
    console.log(error)
  })
// 執行fun1,3秒後將resolve的結果'hello'作為參數回傳給fun2
// fun2接收到'hello',2秒後將resolve的結果'hello'作為參數回傳給fun3
// fun3 印出 'hello'
// 不使用Promsie
function fun1(callback) {
  setTimeout(() => {
    callback('hello')
  }, 3000)
}

function fun2(data, callback) {
  setTimeout(() => {
    callback(data)
  }, 2000)
}

function fun3(data) {
  console.log(data)
}

//
fun1((resultFromFun1) => {
  fun2(resultFromFun1, (resultFromFun2) => {
    fun3(resultFromFun2)
  })
})

範例:傳入的數字是否大於 5

//example
const getSomeData = (num) => {
  return new Promise((resolve, reject) => {
    if (num > 5) {
      resolve('resolve number > 5')
    }
    reject('reject number < 5')
  })
}

getSomeData(10)
  .then((data) => {
    return getSomeData(0)
  })
  .catch((err) => {
    return getSomeData(10)
  })
  .then((data) => {
    console.log(data)
  })
  .finally(() => {
    console.log('finally')
  })

Promise 方法

  1. Promise.all
  • 目的:讓多個 promise 同時執行
  • 參數接受一個可迭代的物件,但通常會傳入 promise 組成的 array 作為參數
  • 每個 promise 完成時,resolve 的值會存入陣列中
  • 如果其中任何一個 Promise 失敗,會回傳第一個失敗的 Promise 物件

範例

//1
const fetchPromise1 = fetch('url1');
const fetchPromise2 = fetch('url2');
const fetchPromise3 = fetch('url3');

Promise.all([fetchPromise1,fetchPromise2,fetchPromise3])
    //return 一個陣列
    .then((responses) => {
    responses.forEach((res)=> {
        console.log(res)
    })
 })
)
//[result1, result2, result3]

//2
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3]).catch((error) => {
  console.log(error); // "Error"
});
  1. Promise.any
  • 只要 Promise array 中的任何一個請求成功就執行.then(),並回傳這個 resolve 的結果
  • 如果所有 promises 都被拒絕,則執行.catch(),回傳 AggregateError,其中包含所有 Promise 的拒絕原因

範例

const promises = [Promise.resolve('hello'), Promise.reject('failed'), Promise.resolve('world')]

Promise.any(promises)
  .then((result) => console.log(result))
  .catch((error) => console.error(error))

// hello
  1. Promise.race
  • 任何一個"陣列傳入參數的 Promise 物件有解決,就會到下一步去

範例

const getSomeData = (num, time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (num > 0) {
        resolve(`${num} > 0`)
      } else {
        reject(`${num} < 0`)
      }
    }, time)
  })
}

Promise.race([getSomeData(10, 500), getSomeData(5, 1000)])
  .then((data) => {
    console.log(data)
  })
  .catch((err) => {
    console.log(err)
  })

// '10 > 0'

async....await

  • Promise 的語法糖,提高程式碼可讀性,少寫很多.then()
  • 所有的 async function 都會 return 一個 Promise Object
  • async 宣告一個非同步函式,await 等待 Promise 的 resolve/reject
  • await 會回傳 Promise resolve/reject 的結果
  • 使用 try / catch 來區分 resolve/reject 要執行的區域
aysnc function myFunc() {
    return 99;
}
let result = myFunc();
console.log(result) //得到一個Promise物件

//取得return的值
result.then((data) => console.log(data)); //99

範例:發出 get 請求

async function fetchProduct(){
    try{
        const response = await fetch(url');
        const data = await response.json();
        console.log(data);
    } catch(err){
        console.log(err)
    }
  }

fetchProduct();

範例:發出 post 請求

async function postData(url = '', data = {}) {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
    return response.json()
  } catch (error) {
    console.error(error)
    throw error // 再次抛出異常,以便上層代碼進行進一步處理
  }
}

// 使用範例:
postData('<https://example.com/api/posts>', { title: 'foo', body: 'bar', userId: 1 })
  .then((data) => {
    console.log(data)
  })
  .catch((error) => {
    console.error(error)
  })

範例:.then() & await 寫法比較

//使用await
async function getApiData() {
  const data = await fetch(url)
  const result = await data.json()
  console.log(result)
}

//使用.then
async function getApiData2() {
  fetch(url)
    .then((data) => data.json())
    .then((result) => {
      console.log(result)
    })
}

範例:使用箭頭函式

const getApiData = async () => {
  const response = await fetch(url)
    .then((res) => {
      return res.json()
    })
    .then((data) => console.log(data))
    .catch((err) => console.log(err))
}

範例:try & catch 也可以使用在同步函式

function double(num) {
  if (isNaN(num)) {
    throw new Error(`${num} is not a number`)
  }
  return num * 2
}

try {
  const x = double(3)
  console.log(x) // 6
} catch (err) {
  console.log(err)
}

參考資料

# JavaScript # ES6