1. 程式人生 > 實用技巧 >nodejs.cn-Node.js-入門教程:瞭解 JavaScript Promise

nodejs.cn-Node.js-入門教程:瞭解 JavaScript Promise

ylbtech-nodejs.cn-Node.js-入門教程:瞭解 JavaScript Promise

1.返回頂部
1、

瞭解 JavaScript Promise

目錄

Promise 簡介

Promise 通常被定義為最終會變為可用值的代理

Promise 是一種處理非同步程式碼(而不會陷入回撥地獄)的方式

多年來,promise 已成為語言的一部分(在 ES2015 中進行了標準化和引入),並且最近變得更加整合,在 ES2017 中具有了asyncawait

非同步函式在底層使用了 promise,因此瞭解 promise 的工作方式是瞭解asyncawait的基礎

Promise 如何運作

當 promise 被呼叫後,它會以處理中狀態開始。 這意味著呼叫的函式會繼續執行,而 promise 仍處於處理中直到解決為止,從而為呼叫的函式提供所請求的任何資料。

被建立的 promise 最終會以被解決狀態被拒絕狀態結束,並在完成時呼叫相應的回撥函式(傳給thencatch)。

哪些 JS API 使用了 promise?

除了自己的程式碼和庫程式碼,標準的現代 Web API 也使用了 promise,例如:

  • Battery API
  • Fetch API
  • Service Worker

在現代 JavaScript 中,不太可能沒有使用 promise,因此讓我們開始深入研究它們。


建立 promise

Promise API 公開了一個 Promise 建構函式,可以使用new Promise()對其進行初始化:

let done = true

const isItDoneYet = new Promise((resolve, reject) => {
  if (done) {
    const workDone = '這是建立的東西'
    resolve(workDone)
  } else {
    const why = '仍然在處理其他事情'
    reject(why)
  }
})

如你所見,promise 檢查了done全域性常量,如果為真,則 promise 進入被解決狀態(因為呼叫了resolve回撥);否則,則執行reject回撥(將 promise 置於被拒絕狀態)。 如果在執行路徑中從未呼叫過這些函式之一,則 promise 會保持處理中狀態。

使用resolvereject,可以向呼叫者傳達最終的 promise 狀態以及該如何處理。 在上述示例中,只返回了一個字串,但是它可以是一個物件,也可以為null。 由於已經在上述的程式碼片段中建立了 promise,因此它已經開始執行。 這對了解下面的消費 promise章節很重要。

一個更常見的示例是一種被稱為 Promisifying 的技術。 這項技術能夠使用經典的 JavaScript 函式來接受回撥並使其返回 promise:

const fs = require('fs')

const getFile = (fileName) => {
  return new Promise((resolve, reject) => {
    fs.readFile(fileName, (err, data) => {
      if (err) {
        reject (err)  // 呼叫 `reject` 會導致 promise 失敗,無論是否傳入錯誤作為引數,
        return        // 且不再進行下去。
      }
      resolve(data)
    })
  })
}

getFile('/etc/passwd')
.then(data => console.log(data))
.catch(err => console.error(err))

在最新版本的 Node.js 中,無需為大多數 API 進行手動地轉換。如果需要 promisifying 的函式具有正確的簽名,則util 模組中有一個 promisifying 函式可以完成此操作。


消費 promise

在上一個章節中,介紹瞭如何建立 promise。

現在,看看如何消費或使用 promise。

const isItDoneYet = new Promise(/* ... 如上所述 ... */)
//...

const checkIfItsDone = () => {
  isItDoneYet
    .then(ok => {
      console.log(ok)
    })
    .catch(err => {
      console.error(err)
    })
}

執行checkIfItsDone()會指定當isItDoneYetpromise 被解決(在then呼叫中)或被拒絕(在catch呼叫中)時執行的函式。


鏈式 promise

Promise 可以返回到另一個 promise,從而建立一個 promise 鏈

鏈式 promise 的一個很好的示例是 Fetch API,可以用於獲取資源,且當資源被獲取時將 promise 鏈式排隊進行執行。

Fetch API 是基於 promise 的機制,呼叫fetch()相當於使用new Promise()來定義 promsie。

鏈式 promise 的示例

const status = response => {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  }
  return Promise.reject(new Error(response.statusText))
}

const json = response => response.json()

fetch('/todos.json')
  .then(status)    // 注意,`status` 函式實際上在這裡被呼叫,並且同樣返回 promise,
  .then(json)      // 這裡唯一的區別是的 `json` 函式會返回解決時傳入 `data` 的 promise,
  .then(data => {  // 這是 `data` 會在此處作為匿名函式的第一個引數的原因。
    console.log('請求成功獲得 JSON 響應', data)
  })
  .catch(error => {
    console.log('請求失敗', error)
  })

在此示例中,呼叫fetch()從域根目錄中的todos.json檔案中獲取 TODO 專案的列表,並建立一個 promise 鏈。

執行fetch()會返回一個響應,該響應具有許多屬性,在屬性中引用了:

  • status,表示 HTTP 狀態碼的數值。
  • statusText,狀態訊息,如果請求成功,則為OK

response還有一個json()方法,該方法會返回一個 promise,該 promise 解決時會傳入已處理並轉換為 JSON 的響應體的內容。

因此,考慮到這些前提,發生的過程是:鏈中的第一個 promise 是我們定義的函式,即status(),它會檢查響應的狀態,如果不是成功響應(介於 200 和 299 之間),則它會拒絕 promise。

此操作會導致 promise 鏈跳過列出的所有被鏈的 promise,且會直接跳到底部的catch()語句(記錄請求失敗的文字和錯誤訊息)。

如果成功,則會呼叫定義的json()函式。 由於上一個 promise 成功後返回了response物件,因此將其作為第二個 promise 的輸入。

在此示例中,返回處理後的 JSON 資料,因此第三個 promise 直接接收 JSON:

.then((data) => {
  console.log('請求成功獲得 JSON 響應', data)
})

只需將其記錄到控制檯即可。


處理錯誤

在上一章節的示例中,有個catch被附加到了 promise 鏈上。

當 promise 鏈中的任何內容失敗並引發錯誤或拒絕 promise 時,則控制權會轉到鏈中最近的catch()語句。

new Promise((resolve, reject) => {
  throw new Error('錯誤')
}).catch(err => {
  console.error(err)
})

// 或

new Promise((resolve, reject) => {
  reject('錯誤')
}).catch(err => {
  console.error(err)
})

級聯錯誤

如果在catch()內部引發錯誤,則可以附加第二個catch()來處理,依此類推

new Promise((resolve, reject) => {
  throw new Error('錯誤')
})
  .catch(err => {
    throw new Error('錯誤')
  })
  .catch(err => {
    console.error(err)
  })

編排 promise

Promise.all()

如果需要同步不同的 promise,則Promise.all()可以幫助定義 promise 列表並在所有 promise 都被解決後執行一些操作

示例:

const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')

Promise.all([f1, f2])
  .then(res => {
    console.log('結果的陣列', res)
  })
  .catch(err => {
    console.error(err)
  })

ES2015 解構賦值語法也可以執行:

Promise.all([f1, f2]).then(([res1, res2]) => {
  console.log('結果', res1, res2)
})

當然,不限於使用fetch,任何 promise 都可以以這種方式使用。

Promise.race()

當傳給其的首個 promise 被解決時,則Promise.race()開始執行並且只執行一次附加的回撥(傳入第一個被解決的 promise 的結果)。

示例:

const first = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, '第一個')
})
const second = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, '第二個')
})

Promise.race([first, second]).then(result => {
  console.log(result) // 第二個
})

常見的錯誤

Uncaught TypeError: undefined is not a promise

如果在控制檯中收到Uncaught TypeError: undefined is not a promise錯誤,則請確保使用new Promise()而不是Promise()

UnhandledPromiseRejectionWarning

這意味著呼叫的 promise 被拒絕,但是沒有用於處理錯誤的catch。 在then之後新增catch則可以正確地處理。

2、
2.返回頂部
3.返回頂部
4.返回頂部
5.返回頂部
1、 http://nodejs.cn/learn/understanding-javascript-promises 2、
6.返回頂部
作者:ylbtech
出處:http://ylbtech.cnblogs.com/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。