Skip to content

JavaScript Promise

ES6 後 JavaScript 開始支援 promise,在沒有 promise 的年代,多個串連的非同步事件處理時常會陷入 callback hell,如 ajax 發送與取得資料。

Callback Hell Callback Hell (Source: The 80/20 Guide to Promises in Node.js)

promise 能以比較簡潔的方式撰寫非同步的處理流程,在以前會使用 callback 的方式處理:

function successCallback(result) {
  console.log("It succeeded with " + result);
}

function failureCallback(error) {
  console.log("It failed with " + error);
}

function doSomething(successCallback, failureCallback) {
  // do something
  if (success){
    successCallback(result);
  }else{
    failureCallback(error);
  }
}

doSomething(successCallback, failureCallback);

但有了 promise 後,則改為回傳一個 Promise,再以 then 設定成功與失敗的處理函式:

function doSomething(){
  return new Promise((resolve, reject) => {
    // do something
    if (success){
      resolve(result);
    }else{
      reject(error);
    }
  });
}
doSomething().then(successCallback, failureCallback);

另一個常見的需求則是串連多個 Promise:

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

利用 awaitasync 也可以讓非同步流程以同步程式的方式表達

function doSomething(){
  return new Promise((resolve, reject) => {
    // do something
    if (true){
      resolve("result1");
    }else{
      reject("error1");
    }
  });
}

function doSomethingElse(result){
  return new Promise((resolve, reject) => {
    // do something
    console.log(result)
    if (true){
      resolve("result2");
    }else{
      reject("error2");
    }
  });
}

function doThirdThing(result){
  return new Promise((resolve, reject) => {
    // do something
    console.log(result)
    if (true){
      resolve("result3");
    }else{
      reject("error3");
    }
  });
}

async function foo() {
  try {
    let result = await doSomething();
    let newResult = await doSomethingElse(result);
    let finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch(error) {
    failureCallback(error);
  }
}

foo()
// > "result1"
// > "result2"
// > "Got the final result: result3"

也可以利用 Promise.all 同時執行多個非同步函數,全部成功時回傳 list,或任一個失敗時進入 catch:

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});

axios 也是以 Promise 為 base 發送 HTTP Request,例如一個基礎的 get request:

const axios = require('axios');

// Make a request for a user with a given ID
axios.get('/user?ID=12345')
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })
  .then(function () {
    // always executed
  });

// Want to use async/await? Add the `async` keyword to your outer function/method.
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}

Reference:

  1. MDN - 使用 Promise
  2. MDN - Promise
  3. Google Developers - JavaScript Promise

Comments