大部分的 Node.js API 在還沒有 promises 的時候就已經建立了,在這些 API 中,使用了回調函數的解決方案。
一般的 Node.js API 使用方式如下:
doSomething(param, (err, result) => {
})
這個也同樣適用於一些庫,例如 node-redis
。當我在一個項目中使用它時,到了某個時候我真的需要把所有的回調函數都移除掉,因為我有太多層次的嵌套回調函數,這種情況下就會陷入所謂的 “回調地獄”。
另外,有時候必須避免使用回調函數,因為你需要從函數中返回函數調用的結果。如果這個結果是通過回調函數返回的,那麼想要獲取結果的唯一方法就是將它作為另一個函數的參數返回,進而持續進行回調函數的操作。
const myFunction = () => {
doSomething(param, (err, result) => {
return result //無法從 `myFunction` 中返回這個結果
})
}
const myFunction = callback => {
doSomething(param, (err, result) => {
callback(result) //不行
})
}
myFunction(result => {
console.log(result)
})
但是,有一個簡單的解決方案,這也是由 Node.js 自身提供的。我們可以通過使用內置的 util
模塊中的 promisify
方法 “promisify” 一個不支援 promises 的函數(以及相應的 async/await 語法)。
const { promisify } = require('util')
然後使用它創建新的函數:
const ahget = promisify(client.hget).bind(client)
const asmembers = promisify(client.smembers).bind(client)
const ahkeys = promisify(client.hkeys).bind(client)
可以看到,我添加了 a
字母來表示「異步」。
現在,我們可以將這個 “回調地獄” 的例子改為更簡潔的版本:
client.hget(`user:${req.session.userid}`, 'username', (err, currentUserName) => {
client.smembers(`followers:${currentUserName}`, (err, followers) => {
client.hkeys('users', (err, users) => {
res.render('dashboard', {
users: users.filter((user) => user !== currentUserName && followers.indexOf(user) === -1)
})
})
})
})
改成更簡潔的寫法:
const currentUserName = await ahget(`user:${req.session.userid}`, 'username')
const followers = await asmembers(`followers:${currentUserName}`)
const users = await ahkeys('users')
res.render('dashboard', {
users: users.filter((user) => user !== currentUserName && followers.indexOf(user) === -1)
})
這在使用一個沒有直接訪問權限的函數時非常有效,比如此例中使用了一個第三方庫。
在底層,promisify
將這個函數包裹在一個 promise 中,並返回它。
你也可以手動這樣做,即從一個函數返回一個 promise,然後再使用 async/await 來使用它:
const handleLogin = (req, user) => {
return new Promise((resolve, reject) => {
req.login(user, (err) => {
if (err) {
return reject({
error: true,
message: err,
})
}
return resolve({
success: true,
})
})
})
}
//...
const resultLogin = await handleLogin(req, user)