JavaScript
d3.js

Promiseを使って、高負荷な演算の前にD3でSVGを出力する

SVGの描画がうまくいかない

以前こんな感じの処理を行おうとしたところ、SVGの描画が思うように行きませんでした。

// d3を使ったSVG出力
const svg = d3.select("body").append("svg");
svg.append("circle").attr("cx", 10).attr("cy", 10).attr("r", 3);

// なにかしら重い計算
const a = new Array();
    for(let i=0;i<150000;i++)
        a.unshift(i*i);

これを実行すると、意図に反してSVGは重い計算が終わったあとに出力されてしまいます。対処法を調べたらPromiseを用いた良さげなものがあったので、メモしておきます。

解決策

const p = new Promise((resolve, reject) => {
    //時間のかかる処理前にやっておきたい処理
    d3.select("body").append("div").text("hello world");
    resolve();
});

p.then(() => {
    return new Promise((resolve, reject) => {
        //事前処理はここにも書ける

        const a = new Array();
        setTimeout(()=>{
            //時間のかかる処理
            for(let i=0;i<150000;i++){
                a.unshift(i*i);
            }
            resolve(a.length);

        }, 100);// 100msは、レンダリングのための猶予の時間
    });
}).then((len) => {
    //次にしたい処理
    console.log(len);
    return;
}).catch(() => {
    console.error('something wrong');
});

これで解決です。

その他Promiseまとめ

promiseを使ったテクニックをまとめておきます

function func0(){
    const str = "";

    const func1 = (str)=>{
        return new Promise((resolve)=>{
            setTimeout(()=>{
                console.log(str + "Alice ");
                resolve(str + "Alice ");
            }, 1500);
        });
    };

    const func2 = (str)=>{
        return new Promise((resolve)=>{
            setTimeout(()=>{
                console.log(str + "Bob ");
                resolve(str + "Bob ");
            }, 1000);
        });
    };

    const func3 = (str)=>{
        console.log(str + "Charlie ");
        return str + "Charlie ";
    };

    func1(str).then(func2).then(func3).then((str)=>{
        console.log(str + "Dan");
    }).catch(()=>{
        console.error("something wrong");
    });
}
func0();

実行結果

Alice
Alice Bob
Alice Bob Charlie
Alice Bob Charlie Dan