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