Node.jsで非同期コードを記述する方法

著者は、オープンインターネット/フリースピーチ基金を寄付の受取先として選びました、それは寄付のための執筆プログラムの一環です。

はじめに

JavaScriptの多くのプログラムでは、コードは開発者が書くときに行ごとに実行されます。これは同期実行と呼ばれ、行は書かれた順序で一つずつ実行されます。しかし、コンピューターに与える命令がすぐに処理される必要はありません。例えば、ネットワークリクエストを送信すると、コードを実行しているプロセスはデータが戻ってくるのを待たなければなりません。この場合、ネットワークリクエストが完了するのを待っている間に他のコードを実行しないと時間が無駄になります。この問題を解決するために、開発者は非同期プログラミングを使用します。非同期プログラミングでは、コードの行が書かれた順序とは異なる順序で実行されます。非同期プログラミングを使用すると、ネットワークリクエストなどの長時間かかるアクティビティの完了を待ちながら他のコードを実行できます。

JavaScriptのコードは、コンピュータープロセス内の単一スレッドで実行されます。そのコードはこのスレッドで同期的に処理され、1度に1つの命令のみが実行されます。したがって、このスレッドで長時間実行されるタスクを行う場合、そのタスクが完了するまで、残りのコードがすべてブロックされます。JavaScriptの非同期プログラミング機能を活用することで、この問題を回避するために長時間実行されるタスクをバックグラウンドスレッドにオフロードできます。タスクが完了すると、タスクのデータを処理するために必要なコードがメインの単一スレッドに戻されます。

このチュートリアルでは、JavaScriptが非同期タスクをどのように管理するかを、イベントループの助けを借りて学びます。これは、別のタスクを待っている間に新しいタスクを完了するJavaScriptの構築物です。その後、非同期プログラミングを使用して、スタジオジブリAPIから映画のリストをリクエストし、データをCSVファイルに保存するプログラムを作成します。非同期コードは、コールバック、プロミス、およびasync/awaitキーワードを使用して3つの方法で記述されます。

注意: この記事の執筆時点では、非同期プログラミングはもはやコールバックのみを使用して行われていませんが、この時代遅れの方法を学ぶことは、JavaScriptコミュニティが今ではプロミスを使用する理由を理解するための大きな文脈を提供できます。async/awaitキーワードを使用すると、プロミスをより簡潔に使用できるため、このキーワードはこの記事の執筆時点ではJavaScriptで非同期プログラミングを行う標準的な方法です。

前提条件

イベントループ

まず、JavaScriptの関数実行の内部動作について学びましょう。これがどのように動作するかを理解することで、非同期コードをより意図的に書くことができるようになり、将来のコードのトラブルシューティングに役立ちます。

JavaScriptインタプリタがコードを実行するとき、呼び出されたすべての関数はJavaScriptのコールスタックに追加されます。コールスタックはスタックであり、アイテムはトップにのみ追加でき、トップから削除できます。スタックは「最後に入れたものが最初に出てくる」またはLIFOの原則に従います。スタックに2つのアイテムを追加すると、最も最近追加されたアイテムが最初に削除されます。

コールスタックを使用した例を示しましょう。JavaScriptが関数functionA()の呼び出しを検出すると、それがコールスタックに追加されます。その関数functionA()が別の関数functionB()を呼び出すと、functionB()がスタックの一番上に追加されます。JavaScriptが関数の実行を完了すると、それがコールスタックから削除されます。したがって、JavaScriptは最初にfunctionB()を実行し、完了したらスタックから削除して、functionA()の実行を完了し、コールスタックから削除します。これが、内部関数が常に外部関数よりも先に実行される理由です。

JavaScriptが非同期の操作、例えばファイルへの書き込みなどに遭遇すると、それをメモリ内のテーブルに追加します。このテーブルには、操作、完了条件、および完了時に呼び出される関数が格納されます。操作が完了すると、JavaScriptは関連する関数をメッセージキューに追加します。キューは、アイテムを下部にのみ追加でき、上部からのみ削除できる別のリスト構造です。メッセージキューでは、2つ以上の非同期操作がそれぞれの関数の実行を待っている場合、最初に完了した非同期操作の関数が最初に実行されるようにマークされます。

メッセージキュー内の関数は、呼び出しスタックに追加されるのを待っています。イベントループは、呼び出しスタックが空かどうかを常に確認するプロセスです。空であれば、メッセージキューの最初のアイテムが呼び出しスタックに移動します。JavaScriptは、コードで解釈される関数呼び出しよりも、メッセージキュー内の関数を優先します。呼び出しスタック、メッセージキュー、およびイベントループの組み合わせ効果により、JavaScriptコードは非同期のアクティビティを管理しながら処理されます。

イベントループの高レベルな理解を持った今、書いた非同期コードがどのように実行されるかがわかりました。この知識を活用して、コールバック、プロミス、およびasync/awaitの3つの異なるアプローチで非同期コードを作成できます。

コールバックを使用した非同期プログラミング

A callback function is one that is passed as an argument to another function, and then executed when the other function is finished. We use callbacks to ensure that code is executed only after an asynchronous operation is completed.

長い間、コールバックは非同期コードを書くための最も一般的なメカニズムでしたが、今ではほとんど使われなくなりました。なぜなら、コードを読みにくくする可能性があるからです。このステップでは、コールバックを使用した非同期コードの例を書いて、他の戦略の効率向上を確認するための基準として使用します。

他の関数でコールバック関数を使用する方法はたくさんあります。一般的に、それらはこの構造を取ります:

function asynchronousFunction([ Function Arguments ], [ Callback Function ]) {
    [ Action ]
}

JavaScriptやNode.jsで構文的に必要とされているわけではありませんが、コールバックを簡単に特定できるようにするために、外部関数の最後の引数としてコールバック関数を配置することが一般的な慣習です。JavaScript開発者がコールバックとして無名関数を使用することも一般的です。無名関数とは、名前を持たずに作成される関数のことです。関数が引数リストの最後に定義されている場合、通常は読みやすくなります。

コールバックを示すために、Studio Ghibliの映画のリストをファイルに書き込むNode.jsモジュールを作成しましょう。まず、JavaScriptファイルとその出力を格納するフォルダを作成します:

  1. mkdir ghibliMovies

その後、そのフォルダに移動します:

  1. cd ghibliMovies

Studio Ghibli APIにHTTPリクエストを行い、その結果をコールバック関数でログに記録します。これには、HTTPレスポンスのデータにアクセスできるライブラリをインストールします。

ターミナルで、npmを初期化して後でパッケージを参照できるようにします:次に、requestライブラリをインストールします:

  1. npm init -y

それから、requestライブラリをインストールします:

  1. npm i request --save

新しいファイルをcallbackMovies.jsとしてテキストエディター(例: nano)で開きます:

  1. nano callbackMovies.js

テキストエディターで、以下のコードを入力します。まずは、requestモジュールを使用してHTTPリクエストを送信することから始めましょう:

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films');

最初の行で、npm経由でインストールされたrequestモジュールをロードします。モジュールはHTTPリクエストを行うことができる関数を返し、その関数をrequest定数に保存します。

request()関数を使用してHTTPリクエストを行います。次に、ハイライトされた変更を追加して、HTTPリクエストからのデータをコンソールに出力しましょう:

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    movies.forEach(movie => {
        console.log(`${movie['title']}, ${movie['release_date']}`);
    });
});

request()関数を使用する際、2つのパラメーターを渡します:

  • リクエストを行おうとしているウェブサイトのURL
  • A callback function that handles any errors or successful responses after the request is complete

コールバック関数には、errorresponsebodyという3つの引数があります。HTTPリクエストが完了すると、引数は結果に応じて自動的に値が設定されます。リクエストが送信に失敗した場合、errorにはオブジェクトが含まれますが、responsebodynullになります。リクエストが正常に送信された場合、HTTPレスポンスはresponseに保存されます。HTTPレスポンスがデータ(この例ではJSONを取得)を返す場合、データはbodyに設定されます。

私たちのコールバック関数はまず、エラーが発生したかどうかをチェックします。コールバック内でまずエラーをチェックすることはベストプラクティスです。これにより、コールバックの実行がデータが欠落した状態で続行されることがありません。この場合、エラーと関数の実行をログに記録します。次に、レスポンスのステータスコードを確認します。サーバーが常に利用可能でない場合や、APIが変更されることにより以前は正常だったリクエストが不正確になることがあります。ステータスコードが200であることを確認することにより、「OK」というリクエストが送信されたことを確認し、期待されるレスポンスであることに自信を持つことができます。

最後に、レスポンスボディをArrayに解析し、各映画をループしてその名前と公開年をログに記録します。

ファイルを保存して終了したら、次のスクリプトを実行します:

  1. node callbackMovies.js

以下の出力が得られます:

Output
Castle in the Sky, 1986 Grave of the Fireflies, 1988 My Neighbor Totoro, 1988 Kiki's Delivery Service, 1989 Only Yesterday, 1991 Porco Rosso, 1992 Pom Poko, 1994 Whisper of the Heart, 1995 Princess Mononoke, 1997 My Neighbors the Yamadas, 1999 Spirited Away, 2001 The Cat Returns, 2002 Howl's Moving Castle, 2004 Tales from Earthsea, 2006 Ponyo, 2008 Arrietty, 2010 From Up on Poppy Hill, 2011 The Wind Rises, 2013 The Tale of the Princess Kaguya, 2013 When Marnie Was There, 2014

スタジオジブリの映画のリストと公開年を受け取りました。現在ログに記録している映画リストをファイルに書き込んで、このプログラムを完成させましょう。

次に、テキストエディターでcallbackMovies.jsファイルを更新し、以下のハイライトされたコードを含めて、映画データを含むCSVファイルを作成します:

callbackMovies.js
const request = require('request');
const fs = require('fs');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    let movieList = '';
    movies.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });

    fs.writeFile('callbackMovies.csv', movieList, (error) => {
        if (error) {
            console.error(`Could not save the Ghibli movies to a file: ${error}`);
            return;
        }

        console.log('Saved our list of movies to callbackMovies.csv');;
    });
});

ハイライトされた変更を注意深く見ると、fsモジュールをインポートしていることがわかります。このモジュールはすべてのNode.jsインストールに標準で含まれており、ファイルに非同期で書き込むwriteFile()メソッドを含んでいます。

movieListにデータをログ出力する代わりに、それを文字列変数に追加します。次に、writeFile()を使用してmovieListの内容を新しいファイルであるcallbackMovies.csvに保存します。最後に、writeFile()関数にコールバックを提供します。この関数は1つの引数errorを持ちます。これにより、ファイルに書き込みできない場合などのケースを処理できます。たとえば、nodeプロセスを実行しているユーザーにその権限がない場合などです。

ファイルを保存して、このNode.jsプログラムを再度次のように実行します:

  1. node callbackMovies.js

ghibliMoviesフォルダー内に、次の内容を持つcallbackMovies.csvが表示されます:

callbackMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

重要な点として、CSVファイルへの書き込みをHTTPリクエストのコールバック内で行うことです。コードがコールバック関数内にあると、HTTPリクエストが完了した後にのみファイルに書き込まれます。CSVファイルを書き込んだ後にデータベースに通信する場合は、writeFile()のコールバック内で呼び出される別の非同期関数を作成します。非同期コードが増えるほど、ネストされるコールバック関数が増えます。

5つの非同期操作を実行し、それぞれが他の操作が完了したときにのみ実行されるようにしたいとします。これをコード化する場合、次のようになります:

doSomething1(() => {
    doSomething2(() => {
        doSomething3(() => {
            doSomething4(() => {
                doSomething5(() => {
                    // 最終的なアクション
                });
            });
        }); 
    });
});

ネストされたコールバックに多くのコードがあると、それらはかなり複雑で読みづらくなります。JavaScriptプロジェクトが規模と複雑さを増すにつれて、この効果はより顕著になり、最終的には管理しきれなくなります。そのため、開発者は非同期操作を処理するためにコールバックを使用しなくなりました。非同期コードの構文を改善するために、Promisesを使用することができます。非同期プログラミングのためのPromisesを使用する

約束事

A promise is a JavaScript object that will return a value at some point in the future. Asynchronous functions can return promise objects instead of concrete values. If we get a value in the future, we say that the promise was fulfilled. If we get an error in the future, we say that the promise was rejected. Otherwise, the promise is still being worked on in a pending state.

通常、Promisesは次の形式を取ります:

promiseFunction()
    .then([ Callback Function for Fulfilled Promise ])
    .catch([ Callback Function for Rejected Promise ])

このテンプレートに示されているように、Promisesもコールバック関数を使用します。約束が果たされたときに実行されるthen()メソッドのためのコールバック関数があります。また、約束が実行されている間に発生したエラーを処理するためのcatch()メソッドのコールバック関数もあります。

プロミスを使用して、Studio Ghibliプログラムをプロミスを使用するように書き直して、実際に手を動かしてみましょう。

AxiosはJavaScript用のプロミスベースのHTTPクライアントなので、さっそくインストールしましょう:

  1. npm i axios --save

好きなテキストエディタで、新しいファイルpromiseMovies.jsを作成します:

  1. nano promiseMovies.js

プログラムはaxiosを使用してHTTPリクエストを行い、次に特別なPromiseベースのfsを使用して新しいCSVファイルに保存します。

promiseMovies.jsにこのコードを入力して、Axiosをロードし、映画APIにHTTPリクエストを送信できるようにします:

promiseMovies.js
const axios = require('axios');

axios.get('https://ghibliapi.herokuapp.com/films');

最初の行で、axiosモジュールをロードし、返された関数をaxiosという定数に格納します。その後、axios.get()メソッドを使用してAPIにHTTPリクエストを送信します。

axios.get()メソッドはPromiseを返します。このPromiseをチェーンして、ジブリの映画のリストをコンソールに印刷します:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        response.data.forEach(movie => {
            console.log(`${movie['title']}, ${movie['release_date']}`);
        });
    })

何が起こっているかを見てみましょう。axios.get()でHTTP GETリクエストを行った後、Promiseが達成された時だけ実行されるthen()関数を使用します。この場合、コールバックの例と同様に映画を画面に表示します。

このプログラムを改善するために、HTTPデータをファイルに書き込むために、ハイライトされたコードを追加します:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })

さらに、fsモジュールを再度インポートします。 fsのインポートの後に.promisesがあることに注意してください。Node.jsには、コールバックベースのfsライブラリのPromiseベースのバージョンが含まれているため、レガシープロジェクトで後方互換性が壊れないようになっています。

HTTPリクエストを処理する最初のthen()関数は、今やコンソールに出力する代わりにfs.writeFile()を呼び出します。Promiseベースのfsのバージョンをインポートしたので、writeFile()関数もまた別のPromiseを返します。そのため、writeFile()のPromiseが達成されたときにもう1つのthen()関数を追加します。

A promise can return a new promise, allowing us to execute promises one after the other. This paves the way for us to perform multiple asynchronous operations. This is called promise chaining, and it is analogous to nesting callbacks. The second then() is only called after we successfully write to the file.

注意: この例では、コールバックの例と同様にHTTPステータスコードをチェックしていませんでした。デフォルトでは、axios はエラーを示すステータスコードを受け取った場合、約束を果たしません。したがって、これを検証する必要はもはやありません。

このプログラムを完成させるには、以下で強調されているように約束にcatch() 関数をチェーンさせてください:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })
    .catch((error) => {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    });

約束のチェーンの中でどれかが果たされない場合、JavaScriptは自動的に定義されていればcatch() 関数に移動します。そのため、非同期操作が2つあるにもかかわらず、catch() 句が1つだけある理由です。

次を実行してプログラムが同じ出力を生成することを確認しましょう:

  1. node promiseMovies.js

ghibliMovies フォルダーには、promiseMovies.csv ファイルが含まれています:

promiseMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

約束を使用すると、コールバックのみを使用するよりもはるかに簡潔なコードを書くことができます。コールバックの約束チェーンは、コールバックのネストよりもクリーンなオプションです。ただし、より多くの非同期呼び出しを行うと、約束チェーンが長くなり、保守が難しくなります。

コールバックと約束の冗長性は、非同期タスクの結果を持つ必要があるときに関数を作成する必要があるために生じます。より良い経験は、非同期の結果を待って関数の外に変数に入れることです。その方法で、変数の結果を関数なしで使用できます。これはasyncawait キーワードを使用して達成できます。

JavaScriptをasync/awaitで書く

async/awaitキーワードは、プロミスを扱う際の代替構文を提供します。プロミスの結果をthen()メソッドで利用する代わりに、結果は他の関数と同様に値として返されます。JavaScriptには、非同期関数であることを示すためにasyncキーワードを使用し、プロミスの結果を返すようにJavaScriptに指示するためにawaitキーワードを使用します。

一般的に、async/awaitの使用は次のようになります:

async function() {
    await [Asynchronous Action]
}

async/awaitを使用すると、Studio Ghibliプログラムを改善できる方法を見てみましょう。テキストエディタを使用して、新しいファイルasyncAwaitMovies.jsを作成して開きます:

  1. nano asyncAwaitMovies.js

新しく開いたJavaScriptファイルには、プロミスの例で使用したのと同じモジュールをインポートして開始します:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

インポートはpromiseMovies.jsと同じです。なぜなら、async/awaitはプロミスを使用するからです。

今度は、非同期コードを持つ関数を作成するためにasyncキーワードを使用します:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {}

新しい関数saveMovies()を作成しますが、その定義の先頭にasyncを含めます。これは重要です。なぜなら、非同期関数内でしかawaitキーワードを使用できないからです。

次のHTTPリクエストを作成して、ジブリAPIから映画のリストを取得するためにawaitキーワードを使用します:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
}

saveMovies()関数では、以前と同様にaxios.get()を使用してHTTPリクエストを行います。今回はthen()関数をチェーンしません。代わりに、その前にawaitを追加します。JavaScriptがawaitを見つけると、axios.get()の実行が完了しresponse変数が設定されるまで、関数の残りのコードを実行しません。他のコードは映画データを保存し、ファイルに書き込むためのものです。

映画データをファイルに書き込みましょう:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
    await fs.writeFile('asyncAwaitMovies.csv', movieList);
}

fs.writeFile()でファイルに書き込む際にもawaitキーワードを使用します。

この関数を完成させるために、約束がスローするエラーをキャッチする必要があります。これを行うには、コードをtry/catchブロックでカプセル化します:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

約束が失敗する可能性があるため、非同期コードをtry/catch節で囲みます。これにより、HTTPリクエストまたはファイル書き込み操作が失敗した場合にスローされるエラーがキャプチャされます。

最後に、非同期関数saveMovies()を呼び出して、プログラムをnodeで実行した際に実行されるようにします。

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

saveMovies();

一目見ると、これは典型的な同期JavaScriptコードブロックのように見えます。関数のやりとりが少なく、少し整然として見えます。これらの小さな調整により、async/awaitを使用した非同期コードがよりメンテナンスしやすくなります。

プログラムのこのイテレーションをテストするには、ターミナルにこれを入力してください:

  1. node asyncAwaitMovies.js

ghibliMoviesフォルダーに、次の内容の新しいasyncAwaitMovies.csvファイルが作成されます:

asyncAwaitMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

これで、JavaScriptのasync/await機能を使用して非同期コードを管理しました。

結論

このチュートリアルでは、JavaScriptが関数を実行し、イベントループで非同期操作を管理する方法を学びました。次に、さまざまな非同期プログラミング技術を使用して、映画データのHTTPリクエストを行った後にCSVファイルを作成するプログラムを作成しました。最初に、廃止されたコールバックベースのアプローチを使用しました。その後、プロミスを使用し、最後にasync/awaitを使用してプロミスの構文をより簡潔にしました。

Node.jsを使用した非同期コードの理解により、API呼び出しに依存するプログラムなど、非同期プログラミングを活用するプログラムを開発できるようになりました。次のリストをご覧ください。公共APIを使用するには、このチュートリアルで行ったように非同期のHTTPリクエストを行う必要があります。さらなる学習のためには、ここで学んだテクニックを実践するためにこれらのAPIを使用するアプリを構築してみてください。

Source:
https://www.digitalocean.com/community/tutorials/how-to-write-asynchronous-code-in-node-js