nodejs.cn-Node.js-入門教程:瞭解 JavaScript Promise
ylbtech-nodejs.cn-Node.js-入門教程:瞭解 JavaScript Promise |
1.返回頂部 |
瞭解 JavaScript Promise
目錄
Promise 簡介
Promise 通常被定義為最終會變為可用值的代理。
Promise 是一種處理非同步程式碼(而不會陷入回撥地獄)的方式。
多年來,promise 已成為語言的一部分(在 ES2015 中進行了標準化和引入),並且最近變得更加整合,在 ES2017 中具有了async和await。
非同步函式在底層使用了 promise,因此瞭解 promise 的工作方式是瞭解async
和await
的基礎。
Promise 如何運作
當 promise 被呼叫後,它會以處理中狀態開始。 這意味著呼叫的函式會繼續執行,而 promise 仍處於處理中直到解決為止,從而為呼叫的函式提供所請求的任何資料。
被建立的 promise 最終會以被解決狀態或被拒絕狀態結束,並在完成時呼叫相應的回撥函式(傳給then
和catch
)。
哪些 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 會保持處理中狀態。
使用resolve
和reject
,可以向呼叫者傳達最終的 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()
會指定當isItDoneYet
promise 被解決(在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.返回頂部 |
3.返回頂部 |
4.返回頂部 |
5.返回頂部 |
6.返回頂部 |
作者:ylbtech 出處:http://ylbtech.cnblogs.com/ 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。 |