從Promise開始的JavaScript異步生活
  • 關於本書
  • 寫作格式
  • 更新日誌
  • 問答與回饋
  • 主要內容
    • 前言
    • 基本概念
    • Promises/A+標準定義
    • Promise 物件建立與基本使用
    • 執行流程與錯誤處理
    • 深入 then 方法
    • Promise.resolve 與 Promise.reject
    • Promise.all 與 Promise.race
    • 執行順序 連鎖(Chaining) 或 分支(Branching)
    • 反樣式(anti-pattern)與最佳實踐
    • 實例與程式碼片段
    • Promise 外部函式庫議題
    • 參考資源
Powered by GitBook
On this page
  • Delay(延時)
  • 包裝 XMLHttpRequest(AJAX)
  • 包裝 jQuery 的$.ajax
  • forEach/for/while
  • 正確用法
  • 呼叫函式將回傳結果轉成 Promise 物件
  • 改寫回調函式

Was this helpful?

  1. 主要內容

實例與程式碼片段

Previous反樣式(anti-pattern)與最佳實踐NextPromise 外部函式庫議題

Last updated 4 years ago

Was this helpful?

Delay(延時)

function delay(ms) {
  ms = Number(ms)
  ms = Number.isNaN(ms) ? +0 : Math.max(ms, +0)

  return new Promise(resolve => setTimeout(resolve, ms))
}

出自

如果要讓delay可以有回傳值,可以用以下的範例:

function delay(ms) {
  return function(result) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        resolve(result)
      }, ms)
    })
  }
}

//使用範例
delay(1000)('hello').then(function(result) {
  console.log(result)
})

包裝 XMLHttpRequest(AJAX)

function ajax(url, method, data) {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest()

    request.responseType = 'text'

    request.onreadystatechange = function() {
      if (request.readyState === XMLHttpRequest.DONE) {
        if (request.status === 200) {
          resolve(request.responseText)
        } else {
          reject(new Error(request.statusText))
        }
      }
    }

    request.onerror = function() {
      reject(new Error('Network Error'))
    }

    request.open(method, url, true)
    request.send(data)
  })
}

//使用範例
ajax('/', 'GET').then(function(result) {
  console.log(result)
})

包裝 jQuery 的$.ajax

function ajax(options) {
  return new Promise(function(resolve, reject) {
    $.ajax(options)
      .done(resolve)
      .fail(reject)
  })
}

//使用範例
ajax({ url: '/' }).then(function(result) {
  console.log(result)
})

forEach/for/while

在 forEach/for/while 等情況時,應該要使用Promise.all,因為它最後可以直接回傳一個陣列值,這提供了一些使用上的便利。

以下的的原本的使用範例,是希望能刪除所有的文件再到下一步:

// 錯誤示範
// 我想要移除所有的文件資料
db.allDocs({ include_docs: true })
  .then(function(result) {
    result.rows.forEach(function(row) {
      db.remove(row.doc)
    })
  })
  .then(function() {
    // 到這裡所有的文件都已經被刪除
  })

正確用法

使用Promise.all,並以map取代forEach。

db.allDocs({ include_docs: true })
  .then(function(result) {
    return Promise.all(
      result.rows.map(function(row) {
        return db.remove(row.doc)
      })
    )
  })
  .then(function(arrayOfResults) {
    // 到這裡所有的文件都已經被刪除
  })

呼叫函式將回傳結果轉成 Promise 物件

function promiseCall(f, ...args) {
  try {
    return Promise.resolve(f(...args))
  } catch (e) {
    return Promise.reject(e)
  }
}

說明如下:

  • 當呼叫函式回傳一個值v時,會回傳一個以v實現的 promise 物件。

  • 當呼叫函式會 throw 出例外e時,會回傳一個以e拒絕一個 promise 物件。

改寫回調函式

基本上這是為了改用回調地獄用的方式,把原本的回調函式程式碼:

//原本的回調函式
function(err, response) { ... }

//-------------------------------

//改用Promise的`then`方法:
.then(function(response) { ... }).catch(function(err) { ... })

另一個改寫的對照簡單範例:

// 原本的回調結構
async1(function(){
    async2(function(){
        async3(function(){
            //....
        });
    });
});

//-------------------------------

// 改為Promise的結構
var task1 = async1();
var task2 = task1.then(async2);
var task3 = task2.then(async3);

task3.catch(function(){
    // 處理task1, task2, task3的例外
})

//-------------------------------

// 使用Promise的連鎖語法
async1(function(){..})
    .then(async2)
    .then(async3)
    .catch(function(){
        // 處理例外
    })

出自

出自

出自

出自

出自

出自

Writing Promise-Using Specifications
Fun with promises in JavaScript
Fun with promises in JavaScript
Fun with promises in JavaScript
We have a problem with promises
Writing Promise-Using Specifications
Staying Sane With Asynchronous Programming: Promises and Generators