タイトルのまんまの話。非同期処理には、コールバック型とPromise型がある。Promise型の方は関数呼び出し側にPromiseを返すので制御がしやすい一方、コールバック側は、関数呼び出し側に返すものがないので制御が難しい(コトが多い)。最近の潮流は Promise/async-await な感じなので、自作する関数はPromise型にするのがよいのだが、使うライブラリにはコールバックのものが多い。つまり、コールバック型のライブラリを自作の関数内で利用し、Promiseを返すという実装パターンは非常に多いのでここで一旦整理しようと思う。
選択肢#01: promisify
Nodeのutilライブラリに、promisifyというユーティリティ関数がある。これは、コールバック型のAPIをPromise型のAPIに変更してくれるという優れものである。ただし、promisifyを使うためには、コールバックの第一引数がerrであるようなものである必要がある。多くのライブラリではこのような実装になっているので使える場面は多いが、使えるかどうかの注意は常にしておくべきだ。
async function myFunction(){
const pFunc = promisify(callbackTypedFunc)
const data = await pFunc()
... do something ...
return someValue
}
選択肢#02: 自作
自分でPromiseをnewしてコールバックの中でPromiseをresolve/rejectするようにコールバック関数を作成しよう。
async function myFunction() {
... do something ...
const callOne = await new Promise((res,rej)=>{
callbackTypedFunc(arg, (err,data)=> {
... do something ....
res(someValue) // or rej(someValue)
})
})
const callTwo = await new Promise((res,rej)=>{
callbackTypedFunc(arg, (err,data)=> {
... do something ....
res(someValue) // or rej(someValue)
})
})
... do something ...
return someValue
}
まとめ
コールバックの方が役立つケースもあるけどやっぱりPromiseだよね。