2015/06/25

【JavaScript】コールバックが終わるまで処理を遅延させる方法

photo by Nicholas D.

自分用に作ったIndexedDBライブラリをテストしようとしたときの話。

以下のような手順で、テストを書いていた。
  1. データベースの作成
  2. データの登録
  3. データの検索
  4. 以下略

問題は「2.データの登録」で発生した。
エラーの内容は、「データベースがまだ作成されていません」的なものだった。
それを解消するために、いろいろ調べたのでまとめていく。


エラーの原因


※ コードはTypeScriptで書いてるけど、基本JavaScriptと同じなので読み替えてほしい。
    テストはJasmineを使って書いている。
class IDBLibrary {
    private _idb: IDBDatabase;
    private _idbInfo: IDBInfo;
    
    /**
     * コンストラクタ
     * @param dbInfo IDBの情報
     */
    constructor(dbInfo: IDBInfo) {
        this._idbInfo = dbInfo;
        var req = indexedDB.open(this._idbInfo.dbName, this._idbInfo.version);
        
        req.onupgradeneeded = () => {
            this._idb = req.result;
            var store = this._idb.createObjectStore(this._idbInfo.storeName, this._idbInfo.key);

            console.log("success:onupgradeneeded");
        }

        req.onsuccess = () => {
            this._idb = req.result;

            console.log("success:onsuccess");
        }
    }
    
    /**
     * 登録
     * @param value     登録する情報(JSON) 
     */
    register(value: any): boolean {
        console.log('register');
        var tran = this._idb.transaction(this._idbInfo.storeName, 'readwrite');
        var store = tran.objectStore(this._idbInfo.storeName);
        
        var req = store.put(value);
        
        req.onsuccess = () => {
            console.log("success:register");
            return true;
        }
    }
    
    /* 以下略 */
}

describe('IDBライブラリのテスト', () => {
    var idb: IDBLibrary;
    beforeEach(() => {
        var idbInfo: IDBInfo = {
            dbName: 'IDBLibraryTest',
            storeName: 'IDBLibrary',
            version: 1,
            key: { keyPath: 'url', autoIncrement: false }
        };
        
        idb = new IDBLibrary(idbInfo);
    });

    afterEach(() => {
            idb.destroy();
    });

    it('データ登録する', () => {
        var videoInfo: IVideoInfo = {
            url: "123/abc",
            title: "データ登録する",
            time: "1h 00m",
            tags: ['tag1', 'tag2', 'tag3']
        };

        var result = idb.register(videoInfo);

        expect(result).toBe(true);
    });
});

これを実行すると、まず26行目でエラーが発生してしまう。
内容は、「transactionが見つかりません」というエラー。

それが解消されても、次は15行目でエラーになってしまう。

ログは以下のようになっている。
> register
> success:onupgradeneeded
> success:onsuccess

コールバックの処理が終わる前に、次の処理を実行してエラーになっていた。



コールバック処理が終わるまで待つ方法


いくつか試してみて、setTimeoutを使うことで落ち着いた。
sleepとかwaitとかsetIntervalとか試してみたけどダメだった。
describe('IDBライブラリのテスト', () => {
    var idb: IDBLibrary;
    beforeEach(() => {
        var idbInfo: IDBInfo = {
            dbName: 'IDBLibraryTest',
            storeName: 'IDBLibrary',
            version: 1,
            key: { keyPath: 'url', autoIncrement: false }
        };
        
        idb = new IDBLibrary(idbInfo);
    });

    afterEach(() => {
        window.setTimeout(
            function() { idb.destroy() },
            2000
            );
    });    

    it('データ登録する', () => {
        var videoInfo: IVideoInfo = {
            url: "123/abc",
            title: "データ登録する",
            time: "1h 00m",
            tags: ['tag1', 'tag2', 'tag3']
        };

        var result;
        window.setTimeout(
            function() { result = idb.register(videoInfo) },
            1000
            );
        expect(result).toBe(true);
    });
});


window.setTimeout(function() { /* 遅延させたい処理 */ }, 遅延時間ms)

と書くことで、特定の処理を遅延させることができる。


私の環境では、sleepやwaitのメソッドを実装したり、setIntervalを使ったりすると、すべての処理(コールバック含め)が遅延してしまい、結果エラーのまま変わらなかった。

なんかもっと良い方法があれば、コメントなりTwitterなりで教えてください!


Jasmineで非同期のテストを行う方法がわかったので、後日「Jasmine2.x以降で非同期テストをする方法」として記事をアップする予定。(時期は未定)




参考サイト




以上

written by @bc_rikko

0 件のコメント :

コメントを投稿