JavaScript
promise

非同期のデータ取得処理をカプセル化する

More than 1 year has passed since last update.

以下のような状況を想定しています。
1. 非同期でデータ取得をしたい
2. 取得したデータに前処理をしてから使用したい
3. 無駄な処理をしないように初回の結果を保存しておきたい
4. 呼び出す側はそれを考えなくても使えるようにしたい

getDataClosure.js
/**
 * 非同期のデータ取得処理
 */
const rawData = Promise.resolve("1,2,3");

/**
 * 前処理
 * @param {*} rawData 
 */
const preprocessing = (rawData) => {
    return rawData.split(",");
};

/**
 * データを取得するクロージャを返す
 * @param {*} rawData 元データ
 * @param {*} preprocessing 前処理をする関数
 */
const getDataClosure = (rawData, preprocessing) => {
    let data;
    return () => {
        console.log('get start');
        if (typeof data === "undefined") {
            // 前処理をする
            return rawData.then((rawData) => {
                console.log('preprocessing');
                data = preprocessing(rawData);
                console.log('get end');
                return data;
            });
        } else {
            // 前処理済だったらそれを返す
            console.log('get end');
            return Promise.resolve(data);
        }
    }
}

// 使用する
const dataClosure = getDataClosure(rawData, preprocessing);
const displayData = () => {
    dataClosure().then((data) => {
        console.log(data);
    });
}

displayData();
setTimeout(displayData, 1000);
setTimeout(displayData, 2000);

実行結果

get start            <- 初回の取得処理
preprocessing        <- 初回なので前処理をする
get end              
[ '1', '2', '3' ]    <- 初回の取得結果
get start            
get end              <- 2回目は前処理をしていない
[ '1', '2', '3' ]    <- 同じ結果が返ってくる
get start            
get end              
[ '1', '2', '3' ]    <- 3回目も同様

上記の欠点

  • 初回の処理が終わる前に2回目以降を呼び出すと前処理が動いてしまう
displayData();
displayData();
displayData();
get start
get start
get start
preprocessing
get end
preprocessing
get end
preprocessing
get end
[ '1', '2', '3' ]
[ '1', '2', '3' ]
[ '1', '2', '3' ]

もっといい書き方があるのだろうか。