18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JavaScript】非同期処理の順番を指定する「Promise」の基本を理解するためのサンプルコード

Last updated at Posted at 2020-01-28

はじめに

JavaScript(ES6)で非同期処理を順番に実行するのに便利なPromiseの基本についてまとめました。

1. Promiseオブジェクトの挙動
2. 通常のコールバック関数
3. 上記コールバック関数をPromiseオブジェクトで書き換え
4. Promiseチェーンで非同期処理の順番を指定

の4つに分けて記載してあります。

環境

OS: macOS Catalina 10.15.1

Promiseのメリット

公式ドキュメントより

現在の JavaScript イベントループの実行完了より前には、コールバックが決して呼び出されない。

非同期処理が完了もしくは失敗した後に then() により登録されたコールバックでも、上記のように呼び出される。

then() を何回も呼び出して複数のコールバックを追加してもよく、それぞれのコールバックは追加順に独立して実行される。

つまり、非同期処理の順番がコントロール出来るというのが最大のメリットですね。

本記事では、そんなPromise

  • 基本的な挙動、
  • 従来のコールバック関数との違い
  • Promiseチェーン

を確認していきます。

1. Promiseオブジェクトの挙動

まずは、Promiseオブジェクトとはどのようなものか?
シンプルな例で確認してみます。

promiseSample.js
const samplePromise = new Promise((resolve, reject) => { //Promiseはresolve, rejectの2つの引数を持つ
  let a = true //ここをtrue/falseで変更して挙動を確認(後述)
  if (a === true) {
    resolve('Success') //trueならresolveの()内が返される
  } else {
    reject('Failed') //falseならrejectの()内が返される
  }
})

//出力部
samplePromise.then((message) => { //thenの引数messageはresolveの()内に記述した内容
  console.log(`This is in the then ${message}`)
}).catch((message) => { //catchの引数messageはrejectの()内に記述した内容
  console.log(`This is in the catch ${message}`)
})

出力例1:a=trueの場合
This is in the then Success
出力例2:a=falseの場合
This is in the catch Failed
出力例3:a=truの場合(タイポ。未定義の変数を代入)
This is in the catch ReferenceError: tru is not defined 

出力例3のように、エラーが発生したら、それが引数となるのにも注目です。

では、次にPromiseで置き換える前のコールバック関数をご確認下さい。

2. 通常のコールバック関数

callbackFunction.js
const cat = false //猫が好きならtrueにして下さい
const dog = false //犬が好きならtrueにして下さい

//コールバックで書いた場合
function callbackSample(callback, errorCallback) { //callback関数, errorCallback関数を引数に持つ
  if (cat && dog) { //犬猫両方好きな場合
    callback('Both dogs and cats are cute :)') //文字列を持つcallbackを返す
  } else if (dog) { //犬だけ好きな場合
    errorCallback({
      name: 'Dog',
      message: 'Wan!!'
    })
  } else if (cat) { //猫だけ好きな場合
    errorCallback({
      name: 'Cat',
      message: 'nyaaaaa~'
    })
  } else { //...
    errorCallback({
      name: 'Oh...',
      message: 'You may not be friend. :('
    })
  }
}

//出力部
callbackSample((message) => { //第一引数はcallback関数の()内に渡した文字列
  console.log(`Success: ${message}`)
}, (error) => { //第二引数はerrorCallback関数の()内に渡した文字列
  console.log(`${error.name}  ${error.message}`)
})

出力例1:cat,dog両方trueの場合
Success: Both dogs and cats are cute :)
出力例2:dogだけtrueの場合
Dog  Wan!!
出力例3:catだけtrueの場合
Cat  nyaaaaa~
出力例4:cat,dog両方falseの場合
Oh...  You may not be friend. :(

これをPromiseオブジェクトを使って書き換えます。

3. Promiseオブジェクトを使って置き換えた場合

promiseFunction.js
const cat = false //猫が好きならtrueにして下さい
const dog = false //犬が好きならtrueにして下さい

// Promiseで変更後
function promiseSample() { //引数に渡していたcallback, errorCallback関数が不要になる
  return new Promise((resolve, reject) => { //Promiseオブジェクトを定義し、その中に全ての処理を書く
    if (cat && dog) {
      resolve('Both dogs and cats are cute :)') //callback => resolveに置き換える
    } else if (dog) {
      reject({ //errorCallback => rejectに置き換える
        name: 'Dog',
        message: 'Wan!!'
      })
    } else if (cat) {
      reject({ //errorCallback => rejectに置き換える
        name: 'Cat',
        message: 'nyaaaaa~'
      })
    } else {
      reject({ //errorCallback => rejectに置き換える
        name: 'Oh...',
        message: 'You may not be friend. :('
      })
    }
  })
}

//出力部
promiseSample() //then, catchをメソッドチェーンで繋げる
  .then((message) => { //thenでresolveの()内を引数messageで受け取る
    console.log(`Success: ${message}`)
  })
  .catch((error) => { //catchでrejectの()内を引数errorを受け取る
    console.log(`${error.name}  ${error.message}`)
  })

出力例1:cat,dog両方trueの場合
Success: Both dogs and cats are cute :)
出力例2:dogだけtrueの場合
Dog  Wan!!
出力例3:catだけtrueの場合
Cat  nyaaaaa~
出力例4:cat,dog両方falseの場合
Oh...  You may not be friend. :(

同様の結果が得られました。

では、次が最後です。非同期処理の順番を指定してみましょう!

4. Promiseチェーンで非同期処理の順番を指定する

以下コードを読む前に、子供が目を閉じて数を数えているシーンを思い浮かべて下さい...きっと理解が進みます。笑

※順番の指定に対して理解しやすくするため、先程までとは違いrejectは使っていません。

methodChain.js
function promiseOne() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('いーち')
    }, 1000) //1秒後に'いーち'という文字列を返す
  })
}

function promiseTwo() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('にーい')
    }, 1000) //1秒後に'にーい'という文字列を返す
  })
}

function promiseThree() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('さーん')
    }, 1000) //1秒後に'さーん'という文字列を返す
  })
}

//出力例
promiseOne()
  .then(one => { //ここのoneにはpromiseOneのresolveの()内にある文字列が入る
  console.log(one)
  return promiseTwo() //ここでreturnした結果が次の引数twoに入る
})
  .then(two => { //ここのtwoにはpromiseTwoのresolveの()内にある文字列が入る
    console.log(two)
    return promiseThree() //ここでreturnした結果が次の引数threeに入る
})
  .then(three => { //ここのthreeにはpromiseThreeのresolveの()内にある文字列が入る
  console.log(three)
})

出力(1秒後)
いーち
出力(2秒後)
いーち
にーい
出力(3秒後)
いーち
にーい
さーん

全て1秒後に文字列を返す関数でしたが、キレイに3秒かけて「いーち、にーい、さーん」と返してくれます。

これで、非同期処理の順番を指定することが出来ました!

以上です!

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

18
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?