JavaScriptで非同期処理を行うには、コールバック関数やPromiseを使う方法があります。
それらに加え、ES7からは「Async/Await」と呼ばれる非同期処理を同期処理的に書く手法が導入されました。

前提としてPromiseの知識が必要ですが、Async/Awaitを知っておくと、より快適に記述することができます。それでは、Async/Awaitについて解説していきます。

Async/Awaitとは

「Async/Await」とは、Promiseによる非同期処理をより効率良く記述することができる構文です。

Async

asyncを関数の前に置くことで、asyncファンクションが生成されます。
asyncファンクションは、常にresolveされたPromiseオブジェクトを返します。

通常のPromise構文と同様に、thenを利用して繋げていくことが可能です。

async function sample() {
  return 123;
}

sample().then(function(value) {
  console.log(value)
});
//123

明示的にpromiseを返すと以下のように記述できます。内容は上記と全く同じもなので、Promise.resolveの記述は必須ではありません

async function sample() {
  return Promise.resolve(123);
}

sample().then(function(value) {
  console.log(value)
});
//123

また、アロー関数で記述することも可能です。

const sample = async () => {
  return 123;
}

sample().then(value => console.log(value));
//123

このようにasync関数を用いることで、new Promiseの記述がなくても、かんたんにPromiseオブジェクトを作成することができます。

Await

awaitは、asyncファンクション内のみで使用できる予約語です。
Promiseが結果を返すまで処理を待機させることが特徴的で、thenメソッドのような役割があります。

async function sample() {
  return 123;
}

async function sample_2() {
  const result = await sample(); //Promiseの結果が分かるまで待機
  console.log(result);
}

sample_2();
//123

また、asyncではなく、通常のPromise構文で返されたPromiseオブジェクトを、async/awaitで繋げることもできます。

async function sample() {
  const promise = new Promise((resolve, reject) => {
    resolve(123)
  });

  const result = await promise; //promiseの結果が分かるまで待機
  console.log(result);
}

sample();
//123

awaitはasyncファンクションの中のみでしか使えないことに注意が必要です。
通常の関数の中でawaitを使うとエラーになります。

function sample() {
  const promise = new Promise((resolve, reject) => {
    resolve(123)
  });
  const result = await promise();
  console.log(result);
}

sample();
//"Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules"

asyncはawaitがなくてもthenメソッドで繋いでいくことができますが、awaitはasyncファンクションの中でないと機能しないため、基本的にasync/awaitはセットであることを意識しておくと良いでしょう。

連続した非同期処理

ここまでで基本的なasync/awaitの使い方が分かりました。
Promiseチェーンになると、よりasync/awaitのメリットを感じることができます。

従来のPromise構文で処理を繋げて書く場合と、async/awaitによる処理を繋げていく場合で比較してみましょう。

以下は、それぞれ1秒後に処理を実行していく例です。

Promiseの場合

const promiseFunc = value => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(value), 1000)
  })
}

promiseFunc(1)
  .then((num) => {
    console.log(num);
    return promiseFunc(2)
  })
  .then((num) => {
    console.log(num);
    return promiseFunc(3)
  })
  .then((num) => {
    console.log(num);
  })
//1
//2
//3

promise

async/awaitの場合

const promiseFunc = value => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(value), 1000)
  });
}

const asyncFunc = async () => {
  console.log(await promiseFunc(1));
  console.log(await promiseFunc(2));
  console.log(await promiseFunc(3));
}

asyncFunc();
//1
//2
//3

async:await

どちらも得られる結果は同じです。

単純な非同期処理であれば、従来のPromise構文でも問題ありませんが、コードが長くなると少し分かりにくい印象です。

一方、async/awaitの記述方では、thenメソッドよりもすっきりしたコードで表現できます。
それだけでなく、非同期処理を行っているのにも関わらず、同期処理のように上から順番に処理しているかのように記述できるため、内容を把握しやすい点もメリットです。

まとめ

非同期処理にはasync/awaitが良くに使われています。
今回の記事では、学習の入り口として、async/awaitがどういったものなのか、イメージに繋がるための解説をしました。

まずは、以下のポイントをおさえておくと良いでしょう。

  • asyncは、常にresolveされたPromiseオブジェクトを返す
  • awaitは、promiseが結果を返すまで処理を待機させる役割がある
  • awaitは、asyncファンクションの中のみで有効

async/awaitの構文だけ見ると、Promiseよりも簡潔に書くことができますが、奥が深いのも事実です。
使いこなすためには、非同期処理の概念からPromiseの理解を深めていくことも大切です。

非同期処理のおさらい