BETA

速習ECMAScript2018を勉強する Promise編

投稿日:2018-11-08
最終更新:2018-11-08

Promise / Async

  • 今回は、非同期処理について見てみたいと思います。 初心者のうちは必要がないんですが、これを知っていないと、初心者のままになりますので、形を覚えて行く感じです。
  • 今回の記事は、少し長めになってしまいました。いままでに3つ記事をUpしていますが、一番行数が少ない記事が一番アクセスが多いという記事あるある状態です。
  • どういうとき、必要かというと、例えばwebサービスのAPIを使いたいとかでしょうか。
  • JSを上から順番にスクリプトを読ませているときは、問題なかったスクリプトが2秒後に変数が入ってくると途端に、うまく動かなくなります。 そのあたりをいい具合に、処理してくれるのがPromiseです。
  • node9 ではエラーになったと思いますので、node10以上を使ってください。
function hoge(value) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {  // 2秒もかかる処理
      if (value) {
        resolve(`** ${value} **`);
      } else {
        reject(new Error('入力値が空'));
      }
    }, 2000)
  })
}

// hoge('佐藤琢磨')
hoge()
  .then(response => {
    console.log(response)
  })
  .catch(error => {
    console.log(`Error: ${error.message}`)
  })
node test.js
** 佐藤琢磨 **

node test.js
Error: 入力値が空
  • 関数を作るときに、返り値に関数を返すものをコールバック関数というらしいです。
  • 擬似的に、ウエイトをかけるのに、よくsetTimeoutが使われます。これがwebからデータを取ってくる処理の代わりです。 fetch、axios等のhttpを処理する命令がここに来ると思われます

  • とりあえず、二秒後に引数valueが無事にとれれば、resolveが実行されます。 失敗すれば、rejectが実行されます。

  • 成功すれば、thenにデータが流れてきます。 失敗すれば catchに流れて来ます。理屈は簡単ですね。

  • Promiseは比較的あたらしい命令ですが、以前から別のコールバック関数が使われています。

function hoge(data, callback) {
  setTimeout( () => { // 2秒もかかる処理
    if (data) {
      callback(data)
    } else {
      callback('引数が空')
    }
  }, 2000)
}

hoge('切磋琢磨', function(value) {
  console.log(value)
})

hoge('', function(value) {
  console.log(value)
})
  • 第二引数に、コールバック関数を渡すことで、2秒かかっても問題なく処理できていることがわかります。

  • どちらを使っても大して手間がかかりませんが、おおにして帰ってきたデータをそのままでは使えないので、加工したい場合が発生します。 その場合は心配いりません、さらにコールバック関数を使えば処理できます。 さらに加工したければ、さらにコールバック関数を使えばいいのです、かなり複雑な処理ができるはずです。

hoge('切磋琢磨', function(value) {
  return foo(value, function(value2)) {
        return boo(value2, function(value3)) {
            return ...
        }
    }
})
  • すいません、面倒そうなので、例は諦めました。
  • 親は、子供の名前ははっきり覚えています、孫もまぁ大丈夫でしょうか、ひ孫は、ちょっと厳しくなってきます、 それ以上はたぶん覚えていないでしょう。 もう少しましな書き方があると思いますが、孫やひ孫ができることには代わりありません。

  • これを、Promiseで書いてみます

function hoge(value) {
  return new Promise((resolve, reject) => {
    setTimeout(() => { // 2秒かかる処理
      if (value) {
        resolve(`*${value}*`);
      } else {
        reject(new Error('入力値が空です'));
      }
    }, 2000);
  })
}
function foo(value) {
  return new Promise((resolve, reject) => {
    setTimeout(() => { // 3秒かかる処理
      if (value) {
        resolve(`-${value}-`);
      } else {
        reject(new Error('入力値が空です'));
      }
    }, 3000);
  })
}
hoge('sato')
  .then(response => { // 2秒かかる処理
    console.log(response)
    return hoge(response); // 成功A: ここに成功した場合簿次の処理を書く
  })
  .then(response => {// 3秒かかる処理
    console.log(response)
    return foo(response) // 成功B: 成功A OK時の実行処理
  })
  .then(response => {// 2秒かかる処理
    console.log(response)
    return hoge(response)  // 成功c: 成功B OK時の処理
  })
  .then(response => {
    console.log(response)
  })
  .catch(error => {
    console.log(`Error: ${error.message}`) // 失敗: いずれかが失敗したら、実行される処理
  });
  • 処理が成功すると、結果が最初の then に戻ってきます。
  • 帰ってきたデータの処理をさらに加工したくなった場合は、thenで次の処理を書けばさらに加工できます。
  • 通常のコールバック関数と比べてみると、孫やひ孫に処理を頼んでいないことがわかります。
  • すべて、息子である一郎、次郎、三郎に頼んでいて、一郎の孫やひ孫に処理を頼んでいません。これなら処理が続いても、同じ労力で済みます。
  • Promiseを併用する書き方に、async という命令が使えます。
    • then の連結と意味合いは変わりません。 どちらを使ってもいいでしょう.
function hoge(value) {
  return new Promise((resolve, reject) => {
    setTimeout(() => { // 2秒かかる処理
      if (value) {
        resolve(`*${value}*`);
      } else {
        reject(new Error('入力値が空です'));
      }
    }, 2000);
  })
}
function foo(value) {
  return new Promise((resolve, reject) => {
    setTimeout(() => { // 3秒かかる処理
      if (value) {
        resolve(`-${value}-`);
      } else {
        reject(new Error('入力値が空です'));
      }
    }, 3000);
  })
}
async function asyncFunc(value) { // thenの代わり
  let result = await hoge(value) // 2秒かかる処理
  console.log(result)
  result = await foo(result); // 3秒かかる処理
  console.log(result)
  result = await hoge(result); // 2秒かかる処理
  console.log(result)
  console.log('処理が終了')
  return result;
}
asyncFunc('sato')
  .then(response => { 
    console.log(response)
  })
  .catch(error => {
    console.log(`Error: ${error.message}`) // 失敗: いずれかが失敗したら、実行される処理
  });
  • 好みの問題ですが code量と見た目てきにも async の方が少しスマートに感じます.

  • あと今まで、順番に処理をする必要があったので、7秒かけて処理をしていましたが、内容によっては、すべて別処理や最後だけ順番待ち等の処理があるかもしれません。

  • 順番に取得する必要がない場合は Promise.all が使えます

    function hoge(value) {
    return new Promise((resolve, reject) => {
      setTimeout(() => { // 2秒かかる処理
        if (value) {
          resolve(`*${value}*`);
        } else {
          reject(new Error('入力値が空です'));
        }
      }, 2000);
    })
    }
    function foo(value) {
    return new Promise((resolve, reject) => {
      setTimeout(() => { // 2秒かかる処理
        if (value) {
          resolve(`-${value}-`);
        } else {
          reject(new Error('入力値が空です'));
        }
      }, 3000);
    })
    }
    Promise.all([ // トータル3秒
    hoge('takuma'),
    foo('sessa'),
    hoge('tomoda'),
    ])
    .then(response => { 
      console.log(response)
    })
    .catch(error => {
      console.log(`Error: ${error.message}`) // 失敗: いずれかが失敗したら、実行される処理
    });
  • 平行して、処理をしてくれるので、トータル3秒です。

  • asyncと組み合わせて、asyncの中で使うと平行処理もできそうですね。
  • このあたりの理解が進むと、次にやりたいことが見えてきそうです。
  • がんばって、習得します。m(_ _)m
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@atoris1192の技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!