SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

NeDB + TypeScriptで、asyncな非同期呼び出しをしてみる

この間使ってみたNeDB、 とても便利なんですが、各種APIがcallback形式のものとなっていて、、安易にコールバック地獄に突入してしまいそうな雰囲気を感じます。
NeDBを使ってNode.js環境でお手軽にNoSQLのDBを使ってみる - SourceChord

せっかくTypeScriptで書くなら、やっぱasync/awaitと一緒に書きたい!!
ということで、asyncな呼び出しができるようなラッパークラスを書いてみました。

コード一式は、以下の場所に上げておきました。 github.com

こんな風に、awaitしながら呼び出すことができます。

async function main() {
    await db.loadDatabaseAsync();

    // 初期データの書き込み
    await db.insertAsync(doc);

    // 全件表示
    var allItems = await db.findAsync({});
    console.log("■全件表示");
    console.dir(allItems);

    // findでの検索
    var searchResult1 = await db.findAsync({ name: "hoge2" });
    console.log("■findでの検索");
    console.dir(searchResult1);
}

やっぱこうやってawaitで待機できると、ちょっと入り組んだ処理になっても、とても理解しやすいですね。

こんな風にPromiseでラップした関数呼び出しを追加したクラスを作り、このクラスを通してasyncな関数を呼ぶようにしています。

import * as NeDBDataStore from "NeDB";

export class AsyncNeDBDataStore extends NeDBDataStore {
    /**
     * コンストラクタ1
     */
    constructor();
    constructor(path: string);
    constructor(options: any);
    constructor(args?: any) {
        super(args);
    }

    // 後で、戻り値を考え直す

    public loadDatabaseAsync(): Promise<void> {
        return new Promise((resolve, reject) => super.loadDatabase((err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        }));
    }

    public ensureIndexAsync(options: Object): Promise<void> {
        return new Promise((resolve, reject) => super.ensureIndex(<any>options, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        }));
    }

    public removeIndexAsync(fieldName: string): Promise<void> {
        return new Promise((resolve, reject) => super.removeIndex(fieldName, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        }));
    }

    public insertAsync<T>(newDoc: T): Promise<T> {
        return new Promise((resolve, reject) => super.insert(newDoc, (err, document) => {
            if (err) {
                reject(err);
            } else {
                resolve(document);
            }
        }));
    }

    public countAsync(query: any): Promise<number> {
        return new Promise((resolve, reject) => super.count(query, (err, n) => {
            if (err) {
                reject(err);
            } else {
                resolve(n);
            }
        }));
    }

    public findAsync<T>(query: any): Promise<Array<T>>;
    public findAsync<T>(query: any, projection: T): Promise<Array<T>>;
    public findAsync<T>(query: any, projection?: T): Promise<Array<T>> {
        if (projection) {
            return new Promise((resolve, reject) => super.find(query, projection, (err, documents) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(documents);
                }
            }));
        } else {
            return new Promise((resolve, reject) => super.find(query, (err, documents) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(documents);
                }
            }));
        }
    }


    public findOneAsync<T>(query: any): Promise<T>;
    public findOneAsync<T>(query: any, projection: T): Promise<T>;
    public findOneAsync<T>(query: any, projection?: T): Promise<T> {
        if (projection) {
            return new Promise((resolve, reject) => super.findOne(query, projection, (err, document) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(document);
                }
            }));
        } else {
            return new Promise((resolve, reject) => super.findOne(query, (err, document) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(document);
                }
            }));
        }
    }


    public updateAsync<T>(query: any, updateQuery: any, options: any): Promise<{ numberOfUpdated: number, affectedDocuments: any, upsert: boolean }> {
        return new Promise((resolve, reject) => super.update(query, updateQuery, options, (err, numberOfUpdated, affectedDocuments, upsert) => {
            if (err) {
                reject(err);
            } else {
                resolve({ numberOfUpdated, affectedDocuments, upsert });
            }
        }));
    }

    public removeAsync<T>(query: any): Promise<number>;
    public removeAsync<T>(query: any, options: any): Promise<number>;
    public removeAsync<T>(query: any, options?: any): Promise<number> {
        if (options) {
            return new Promise((resolve, reject) => super.remove(query, options, (err, n) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(n);
                }
            }));
        } else {
            return new Promise((resolve, reject) => super.remove(query, (err, n) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(n);
                }
            }));
        }
    }
}