CalbackとPromiseとasync/awaitの非同期処理


JavaScriptと非同期


JAvascriptエンジンは싱글 스레드です.実行コンテキストスタックは1つしかありません.これは、1つのタスクしか処理できず、2つ以上の関数を同時に並列に処理できないことを意味します.
console.log(1);

setTimeout(() => console.log(2), 0);

console.log(3);
上記のコードを実行すると、コンソールで撮られた順序は1.2.3ではなく、1.2.3です.
単一スレッドjavascriptがコード1,2,3を順次実行する場合、settimeout関数は実行中に他のコードを読み取ることができずブロックされ、console.log(3)を実行します.この動作は性能に大きな問題がある.
したがって、settimeout関数は非同期です.非同期関数は、実行時にすべてのコード、すなわちコールスタックがクリアされた後に実行されます.(どのような内部プロセスで操作されるかを理解するには、タスクキューとイベントループを参照してください.)これがsettimeoutを0 msに設定しても、常に最後に操作する理由です.

Callback

function add5(num, callback) {
  console.log(num); // 0, 5, 10, 15
  setTimeout(() => callback(num + 5), 1000);
}

add5(0, function(a) {
  add5(a, function(b) {
    add5(b, function(c) {
      add5(c, function(d) {
        console.log(d); // 20
      })
    })
  })
});
上述したように、非同期関数は、実行時にすべてのコードを実行し、実行します.
非同期関数が実行された後、「次へ」が非同期処理の結果に基づいてコードを実行することを望む場合、以前から上述したコールバック方式で処理されていた.
コールバック関数とは、関数(add5)のパラメータを関数(callback)に渡し、この関数(add5)内で呼び出される関数(callback)である.
add 5関数でcallback関数を実行するので、add 5関数のコード実行後、callback関数の実行の「順序は保証されています.」

これは、上記のコードを実行した結果です.settimeout関数で設定した1秒の時間の後、5を順次加算した結果が表示されます.非同期処理はきちんと保障されている.
しかし,コールバック関数のコードを見ると問題点がわかり,デップはますます深くなっているため毒性が悪い.これをコールバックhell(callback hell)と呼びます.

Promise

function add5(num) {
  console.log(num); // 0, 5, 10, 15
  return new Promise(resolve => {
    setTimeout(() => resolve(num + 5), 1000);
  });
}

add5(0)
  .then((a) => add5(a))
  .then((b) => add5(b))
  .then((c) => add5(c))
  .then((d) => {
    console.log(d); // 20
  });
上記の問題により,ES 6にはPromiseの文法が出現した.
Promiseはコンストラクタです.コンストラクション関数Promiseを呼び出すとPromiseインスタンスオブジェクトが返されます.
Promiseオブジェクトには、thenと呼ばれる方法があります.その後、このメソッドを呼び出すと、コールバック関数をパラメータとすると、Promiseは解析として受信し、非同期処理を実行します.上記コールバック例のcallbackパラメータは、Promise例の解析パラメータの動作と似ています.

resolve  :  (a) => add5(a)
num + 5  :  a
上記のように対応していれば、コードがより読みやすくなるはずです.
使用であってもよく、thenメソッドがPromiseオブジェクトを再び返すため、thenを連続的に使用することができる.
このとき、従来のthenメソッドの因数が入るコールバック関数((a) => add5(a))のreturn値(add5(a))は、thenメソッドのコールバック関数((b) => add5(b))のパラメータ(b)に入る.すなわち、add5(a)の値はbである.

Promiseでのエラー処理

function add5(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(num + 5), 1000);
		if (num === 10) reject('num cannot be 10');
  });
}

add5(0)
  .then((a) => add5(a))
  .then((b) => add5(b))
  .then((c) => add5(c)) // error 발생 위치
  .catch((err) => console.log(err)) // num cannot be 10
  .then((d) => add5(d));
拒否関数を呼び出すと、Promise操作が拒否されたことを示し、catchメソッドがエラーをキャプチャするように呼び出されます.
then−resolve関係と同様にcatchメソッドパラメータとしてのコールバック関数がexecuteとして呼び出される.したがって、拒否関数としてのパラメータの値('num cannot be 10')はcatchパラメータコールバック関数のパラメータ(err)の位置に入る.
直ちにエラー処理を行わずに次のthenコードを行う場合は、最後にcatchを使用します.
add5(0)
  .then((a) => add5(a))
  .then((b) => add5(b))
  .then((c) => add5(c)) // error 발생 위치
  .then((d) => add5(d))
  .catch((err) => console.log(err));
実際、thenは2つのパラメータを受け入れることができ、最初のパラメータはresovle関数であり、2番目のパラメータの位置はexecute関数である.しかし、この毒性もよくなく、主にcatch文でエラー処理を行う.
Promise.resolve()
  .then(() => {
    throw new Error('으악!');
  })
  .then(() => {
    console.log('실행되지 않는 코드');
  }, (error) => {
    console.error('onRejected 함수가 실행됨: ' + error.message);
  });
既存  try-catchを使用して例外処理を行うことができますが、JavaScriptはPromiseのcatchを使用するように警告メッセージを出力します.
+)この文章の主題とはあまり関連がないが,参考として以下の文法はいずれもJavaScriptで併用できる文法である.
add5(0).then(add5);
add5(0).then((a) => add5(a));
add5(0).then((a) => { return add5(a) });
add5(0).then(function(c) { return add5(c) });

Async Await

function add5(num) {
  console.log(num); // 0, 5, 10, 15
  return new Promise(resolve => {
    setTimeout(() => resolve(num + 5), 1000);
  });
}

async function print() {
  const a = await add5(0);
  const b = await add5(a);
  const c = await add5(b);
  const d = await add5(c);
  console.log(d); // 20
}

print();
async await構文は、Promiseよりも遅いECMAScript 2017に現れる構文です.
async await構文は、同期コードを記述するように非同期コードをほとんど使用することができ、可読性が最も優れている.
waitキーワードの後ろにPromiseオブジェクトがあります.保留中のPromiseステータスは、保留中の日付を待機し、完了後に次のコードを実行します.awaitを書くときはasyncを書かなければなりません.async付きprint関数はPromiseオブジェクトを返します.

Async Awaitでのエラー処理

function add5(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(num + 5), 1000);
    if (num === 10) reject('num cannot be 10');
  });
}

async function print() {
  try {
    const a = await add5(0);
    const b = await add5(a);
    const c = await add5(b);
    const d = await add5(c);
  } catch (err) {
    console.log(err); // num cannot be 10
  }
}

print();
エラー処理の一般的な方法try-catchを使用して、エラーを処理することができる.
const a = await add5(0).catch((err) => console.log(err));
const b = await add5(a).catch((err) => console.log(err));
add 5関数はPromiseオブジェクトを返すので、直接関数にcatch文を使用することもできますが、実際の操作ではtry catch文が一般的に使用されます.

参考資料


JavaScript非同期通信Calback、Promise、Async/Awaitについて
Mdn Using Promises