72466
## 問題

画像から色の情報を取得したく [color-thies](https://lokeshdhakar.com/projects/color-
thief/#getting-started) というライブラリーを使用しています。

途中で画像のsrcパスを書き換えて別の画像に差し替える処理があるのですが、その際タイミングが合わず前の画像の値になってしまいます。

## プログラム動作

[![画像の説明をここに入力](https://i.stack.imgur.com/pagxf.png)](https://i.stack.imgur.com/pagxf.png)

inputのフォーカスが別の要素に移った際に下記のイベントリスナーが発火します。それでURLの値があればサムネイルを取得します。その画像の色を取得したくcolor-
thiesを使用しています。 サムネイルはダウンロードしてローカルのpathをimgのsrcを書き換えて表示しています。  
そのタイミングが合わなくて前に設定されている色が取得されてしまいます。おそらくsrcを書き換える前に
`colorThief.getColor(thumbnailIMG)`
が実行されているためだと思います。画像の主に使用される色がRGBの値が配列になって返ってきます。

**dlcard**

```

    <div class="dlcard">
        <div class="dlcard__title">
        　　<svg class="icon icon-youtube"><use xlink:href="#icon-youtube2"></use></svg>
        　　<div class="dlcard__videoName">Supreme bot</div>
        </div>
        <div class="dlcard__thumbnail">
            <img src="images/error.png" alt="">
        </div>
        <div class="dlcard__url">
        　<input type="url" name="url01" size="18" placeholder="URL：https://www.youtube.com/watch?v=">
        </div>
    </div>
    
```

**javascript**  
addEventListenerなのですが、まだリファクタリング出来ていなくてとても汚いです。申し訳ありません。

```

    this.DOM.dlcardWrap.addEventListener("blur", 
                function (elm) {
                    const input = elm.target;
                    if (input.parentElement.classList.contains("dlcard__url")) {
                        if (input.value.match(/^https:\/\/www\.youtube\.com\/watch\?v=.*/)) {
                            const url = new URL(input.value)
                            let pairs = url.search.substring(1).split('&');
                            let params = {}
                            for(let pair of pairs) {
                                let kv = pair.split('=');
                                params[kv[0]] = kv[1];
                            }
                            let videoID = params.v;
                            let dlcard = input.closest('.dlcard');
                            let thumbnailIMG = dlcard.childNodes[3].childNodes[1];
                            if(videoID) {
                                async function run() {
                                    let title = await eel.get_title(input.value)();
                                    dlcard.querySelector('.dlcard__title > .dlcard__videoName').innerText = title;
                                }
                                run();
    
                                async function getSrc() {
                                    let url = await eel.get_src(videoID)();
                                    thumbnailIMG.src = url;
                                }
                                getSrc();
                                // Make sure image is finished loading
                                const colorThief = new ColorThief();
                                if (thumbnailIMG.complete) {
                                    console.log(thumbnailIMG);
                                    const thumColor2 = colorThief.getColor(thumbnailIMG);
                                    console.log(thumColor2);
                                }
                                
                            }
                        }else{
                            input.value = '';
                            input.placeholder = 'URLが違います。';
                        }
                    }else{
                        console.log('dlcard_urlが取得出来ていないです。')
                    }
                        
    
                }, true);
            
        }
    
```

Javascriptについて勉強中で詳しい方助けて下さいお願いします。
CC BY-SA 4.0
2020-12-06T08:39:35.877
72450
2020-12-07T05:20:06.787
2020-12-07T05:20:06.787
22565
22565
question
javascript html
JavascriptでDOM処理のタイミングが合わず値を上手く処理出来ない。
動作確認していませんが、おそらく問題は「`async`関数を`await`していない」ことです。

### どうしたら直るか

  * イベントハンドラを `async` にする。中で `await` を使うため。

```

    this.DOM.dlcardWrap.addEventListener("blur", 
                function (elm) {
    
```

⬇

```

    this.DOM.dlcardWrap.addEventListener("blur", 
                async function (elm) {
    
```

  * `run()`の呼び出しを`await`する。

```

                                run();
    
```

⬇

```

                                await run();
    
```

  * `getSrc()`の呼び出しを`await`する。

```

                                getSrc();
    
```

⬇

```

                                await getSrc();
    
```

または、イベントハンドラが `async` になれば `eel` に対する `await` ができるようになるので、関数 `run()` と
`getSrc()` は削除して呼び出し側に展開してしまえばよいです。

例:

```

    this.DOM.dlcardWrap.addEventListener("blur", 
                async function (elm) {
    ...
                            if(videoID) {
                                let title = await eel.get_title(input.value)();
                                dlcard.querySelector('.dlcard__title > .dlcard__videoName').innerText = title;
    
                                let url = await eel.get_src(videoID)();
                                thumbnailIMG.src = url;
                                // Make sure image is finished loading
                                ...
    
    
```

### 何が起きていたのか

`async`関数を呼び出すと、その関数内部の非同期処理を進めるためのPromiseが返ります。関数の実行が全て終る前に呼び出し元に帰ってきます。関数の実行がすべて終るまで待つためには、`await`する必要があります。

`getSrc()`で`await`していないと、`thumbnailIMG.src` への代入をする前にColorThiefの処理に進んでいまいます。

### その他

  * `if (thumbnailIMG.complete) {` はコード実行時の状態を調べているだけで、画像のロード完了を待っていません。`thumbnailIMG`の`'load'`イベントと`'error'`イベントを待つ必要があります。

  * 異常系を最初に処理するとインデントが深くなりすぎずコードがすっきりします。例:

```

                    if (input.parentElement.classList.contains("dlcard__url")) {
                        ... 長いコード ...
                    } else {
                        console.log('dlcard_urlが取得出来ていないです。')
                    }
    
```

⬇

```

                    if (!input.parentElement.classList.contains("dlcard__url")) {
                        console.log('dlcard_urlが取得出来ていないです。');
                        return;
                    }
                    ... 長いコード ...
    
```
CC BY-SA 4.0
2020-12-07T01:30:48.003
72466
2020-12-07T05:08:54.513
2020-12-07T05:08:54.513
3475
3475
72450
answer