もりけん塾 JavaScript課題9 async/awaitを使った非同期処理

もりけん塾 JavaScript課題9 async/awaitを使った非同期処理

今回は、課題7でやった非同期処理をasync/awaitで実行する課題です。

async/awaitは、非同期処理を、より簡単にわかりやすく書くことができるものらしいです。便利ですね!asyncを関数の前につけると、その処理はPromiseオブジェクトを返します。awaitでthenの代用ができます。

お題

async awaitを使って同じことをやってください。rejectは考慮しないでいいです。問題7をasync awaitを使って書いてください。

課題7学習記録はこちら

課題をやる前に考えたこと

前回、変数や関数の命名について、どうすべきかとても悩んでいました。後からコードを見たときに理解できない名前をつけたくないと思ったからです。

普段のお仕事で、いつもお世話になっているエンジニアさんが書いたコードは、JavaScript初心者の自分でも、読みやすいと感じるコードです。何にやっているか、何についての変数なのか、すぐに理解できるように、関数名や変数名がついています。

自分のコードは、それに比べて読みにくい、理解しにくい感じがしたので、エンジニアさんは、おそらく、読みやすい工夫をしているはず、と考えました。理解しやすく書けるコツが知りたくて、色々調べていたところ、もりけん先生から「リーダブルコード」という本をおすすめしてもらい、読みました。

リーダブルコードについては、そのうちまた感想を書こうと思います。とても読みやすく、どういうコードが、読みやすいコードなのかを知ることができ、もやもやが晴れました!

変数名・関数名をつける参考にしたサイト:

課題の準備

それでは、async/awaitを実装していきます。async/awaitは、非同期処理の構文のことで、Promiseを利用するよりも、簡潔に非同期処理を書くことができます。

async functionは呼び出されるとPromiseを返します。returnされると返り値はresolveされ、例外などをthrowするとその値をrejectします。

awaitは、通常の関数の中では使うことができません。async function内でのみ使うことができます。awaitは、Promiseの結果(resolvereject)が返されるまで処理を一時停止します。関数の前にawaitをつけると、その関数のPromiseの結果が返されるまで待機することができます。

非同期処理について詳しく説明されているサイト:

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function

async/waitについてわかりやすかったサイト:

https://medium.com/acompany/javascript%E3%81%AEasync-await%E3%82%92%E5%AE%8C%E5%85%A8%E3%81%AB%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B-6552337eb92

最初にプルリクエストしたコード

今回は、senさんと、ちひろさんにレビューして頂きました!いつも本当にありがとうございます!

関数名が迷走している感じですが、そのまま載せます。

function showLoadingImg(){
    const loadingImg = document.createElement('img');
    loadingImg.src = "loading-circle.gif";
    loadingImg.alt = "ローディング画像";
    document.getElementById('js-loading').appendChild(loadingImg);
}

function removeLoadingImg(){
    document.getElementById('js-loading').remove();
}

function showCategories(result){
    const fragment = document.createDocumentFragment();
    for(const list of result){
        const li = document.createElement('li');
        const a = document.createElement('a');
        const img = document.createElement('img');
        a.textContent = list.text;
        a.href = `/${list.to}`;
        img.src = list.img;
        img.alt = list.alt;
        fragment.appendChild(li).appendChild(a).appendChild(img);
    }
    document.getElementById('js-list').appendChild(fragment);
}

async function SetCategoriesAfter3Seconds(){
    return new Promise(resolve => {
        const categories = [
            {to: "bookmark.html", img: "1.png", alt:"画像1", text: "ブックマーク"},
            {to: "message.html", img: "2.png", alt:"画像2", text: "メッセージ"}
        ];
        setTimeout(() => {
            resolve(categories);
            },3000);
    });
}

async function awaitGetCategories(){
    let result = await SetCategoriesAfter3Seconds();
    removeLoadingImg();
    showCategories(result);
}

showLoadingImg();
awaitGetCategories();

awaitを使っていない関数にも、asyncをつけていたので、senさんに、ここはasyncは必要ないことを、教えて頂きました。asyncをつけると、必ずPromiseでラップされるので、PromiseをさらにPromiseでラッピングしてました。。自分でも、なぜつけたのか謎です。

ということは、awaitをつけると、Promiseを省略して書けるのかな?と思いましたが、今回はよくわからなくなりそうだったので、やめました。

async function SetCategoriesAfter3Seconds(){
    return new Promise(resolve => {
        const categories = [
            {to: "bookmark.html", img: "1.png", alt:"画像1", text: "ブックマーク"},
            {to: "message.html", img: "2.png", alt:"画像2", text: "メッセージ"}
        ];
        setTimeout(() => {
            resolve(categories);
            },3000);
    });
}

ここはasyncを削除しました。

function SetCategoriesAfter3Seconds(){
    return new Promise(resolve => {
        const categories = [
            {to: "bookmark.html", img: "1.png", alt:"画像1", text: "ブックマーク"},
            {to: "message.html", img: "2.png", alt:"画像2", text: "メッセージ"}
        ];
        setTimeout(() => {
            resolve(categories);
            },3000);
    });
}

関数をまとめて書いたのは、senさんに、いいアイディアとほめていただきました。嬉しいです!

async function awaitGetCategories(){
    let result = await SetCategoriesAfter3Seconds();
    removeLoadingImg();
    showCategories(result);
}

showLoadingImg();
awaitGetCategories();

ここの関数の中に、showLoadingImg();をまとめたらどうか?と提案されました。

function SetCategoriesAfter3Seconds(){
    return new Promise(resolve => {

確かにその方が、わかりやすいので、まとめました。

function SetCategoriesAfter3Seconds(){
    showLoadingImg();
    return new Promise(resolve => {

関数名と中身の処理がチグハグになってしまうので、関数名をもっと汎用的なものに考え直す必要がありそうです。次回の課題にします。

ちひろさんから、どうしてここでletを使うのか?と質問されました。

async function awaitGetCategories(){
    let result = await SetCategoriesAfter3Seconds();
    removeLoadingImg();
    showCategories(result);
}

特に、letを使わなくても、問題ない部分だったので、constに直しました。(どうしてもletを使わないといけない場合を除き、constを使うのがいいそうです。)

最終的なコード

頂いたコメントを元に修正して、approveいただきました。

function showLoadingImg(){
    const loadingImg = document.createElement('img');
    loadingImg.src = "loading-circle.gif";
    loadingImg.alt = "ローディング画像";
    document.getElementById('js-loading').appendChild(loadingImg);
}

function removeLoadingImg(){
    document.getElementById('js-loading').remove();
}

function showCategories(result){
    const fragment = document.createDocumentFragment();
    for(const list of result){
        const li = document.createElement('li');
        const a = document.createElement('a');
        const img = document.createElement('img');
        a.textContent = list.text;
        a.href = `/${list.to}`;
        img.src = list.img;
        img.alt = list.alt;
        fragment.appendChild(li).appendChild(a).appendChild(img);
    }
    document.getElementById('js-list').appendChild(fragment);
}

function SetCategoriesAfter3Seconds(){
    showLoadingImg();
    return new Promise(resolve => {
    const categories = [
        {to: "bookmark.html", img: "1.png", alt:"画像1", text: "ブックマーク"},
        {to: "message.html", img: "2.png", alt:"画像2", text: "メッセージ"}
    ];
    setTimeout(() => {
        resolve(categories);
        },3000);
    });
}

async function awaitGetCategories(){
    const result = await SetCategoriesAfter3Seconds();
    removeLoadingImg();
    showCategories(result);
}

awaitGetCategories();

今回学んだこと

  • async/awaitは、Promiseよりも簡単に非同期処理を書くことができる
  • awaitはthenの代用になる
  • awaitはasyncをつけた関数の中でしか使えない

まい

フロントエンドエンジニア目指して、勉強中です。

その他カテゴリの最新記事