Node.js v8.1 で util.promisify
っていう関数が追加されてたんですね。
古き良き伝統にしたがったコールバック形式の関数を Promise を返す形にしてくれる。つまり await 出来るようになる!
ということで、今時コールバック形式のライブラリなんて…と思ってたら azure-storage
がそれでした。
ということで使ってみよう。
使い方は簡単。
const f = util.promisify(hogehoge);
のようにコールバック形式の関数を渡してやると Promise を返す関数になるので…
await f();
とすれば OK。
じゃぁこんな感じに使えるかな。
import express from 'express'; import util from 'util'; import azure from 'azure-storage'; interface EntityType { PartitionKey: azure.TableService.EntityProperty<string>; RowKey: azure.TableService.EntityProperty<string>; Value: azure.TableService.EntityProperty<string>; } // Azure ストレージエミュレーターにつなぐ const client = azure.createTableService('AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;DefaultEndpointsProtocol=http;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;'); async function getAll() { await util.promisify(client.createTableIfNotExists).bind(client)('test'); const query = new azure.TableQuery().where('PartitionKey eq ?', 'a').select('Value'); // 本当は util.promisify(client.queryEntities<EntityType>) みたいにしたかった。この場合どうするのが正解なんだろう return await util.promisify(client.queryEntities).bind(client)('test', query, null as any) as azure.TableService.QueryEntitiesResult<EntityType>; } const app = express(); app.get('/', async (req, res) => { const result = await getAll(); res.json(result.entries.map(x => x.Value._)); }); app.listen(3000);
注意点は、関数内部の this をちゃんと bind で指定してあげることくらいかな?
適当にデータつっこんだテーブルを用意して実行して localhost:3000 にアクセスすると…
返ってきた!!
実際使うとしたら都度都度 util.promisify するのではなく、都合のいい感じのラッパークラスを用意する感じかなぁ?
import express from 'express'; import util from 'util'; import azure from 'azure-storage'; interface EntityType { PartitionKey: azure.TableService.EntityProperty<string>; RowKey: azure.TableService.EntityProperty<string>; Value: azure.TableService.EntityProperty<string>; } // こういうラッパークラスにめんどくさい処理は押し込んでおいて… class TableService { constructor(private tableService: azure.TableService) { } public createTableService = util.promisify(this.tableService.createTableIfNotExists).bind(this.tableService); private _queryEntities = util.promisify(this.tableService.queryEntities).bind(this.tableService); public async queryEntities<T>(tableName: string, query: azure.TableQuery, token: azure.TableService.TableContinuationToken) { return await this._queryEntities(tableName, query, token) as azure.TableService.QueryEntitiesResult<T>; } } const innerClient = azure.createTableService('AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;DefaultEndpointsProtocol=http;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;'); const client = new TableService(innerClient); async function getAll() { // 使う側は意識しなくてよくする await client.createTableService('test'); const query = new azure.TableQuery().where('PartitionKey eq ?', 'a').select('Value'); return await client.queryEntities<EntityType>('text', query, null as any); } const app = express(); app.get('/', async (req, res) => { const result = await getAll(); res.json(result.entries.map(x => x.Value._)); }); app.listen(3000);
まとめ
ライブラリー側で await 出来るように Promise 返してくれるようにしてほしさある。