学習記録

学習したことをより深く理解するために、アウトプットする場所として利用しています。

async/awaitについて理解する

async/awaitはPromiseをより直感的に記述できるようにしたもの(ES8)
asyncと書けば必ずPromiseが返ってくることが担保されている。
awaitはPromiseを返却する関数の非同期処理が完了するまで待って次の処理に移行するようにする。

Promiseの処理をasync/awaitに書き換える

Promiseの処理

function sleep(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(val++);
      resolve(val);
    }, 1000);
  });
}

sleep(0)
  .then((val) => {
    return sleep(val);
  })
  .then((val) => {
    return sleep(val);
  })
  .then((val) => {
    return sleep(val);
  })
  .then((val) => {
    return sleep(val);
  })
  .then((val) => {
    return sleep(val);
  });

console.log("global end");

// Promise
// global end
// 0
// 1
// 2
// 3
// 4
// 5

上記のPromise処理をasync/awaitに置き換える

function sleep(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(val++);
      resolve(val);
    }, 1000);
  });
}

async function init() {
  let val = await sleep(0);
  val = await sleep(val);
  val = await sleep(val);
  val = await sleep(val);
  val = await sleep(val);
  val = await sleep(val);
}

init();

console.log("global end");

// 結果は同上

awaitを使用すると、返り値としてresolveが返ってくる。 awaitを付けないと、Promiseオブジェクトが返ってくる(returnされているので)

async自体もPromsieのインスタンスを返す。(console.log(init())で確認できる)
なので、init()にも.thenを繋げることができ、asyncの処理が終了したら.thenの中が実行する。 また、async内でreturn valをすると、.then(val)に値を引き渡すことができる。

// 省略

async function init() {
  let val = await sleep(0);
  val = await sleep(val);
  val = await sleep(val);
  val = await sleep(val);
  val = await sleep(val);
  val = await sleep(val);
  return val
}

init().then((val) => {
  console.log("hello");
});

参考

www.udemy.com

MicroTasksとMacroTasksの実行順番について

MicroTasksとMacroTasksについて

  • MicroTasksの処理が全て完了したら、MacroTasksの実行が開始される(MicroTasks優先)

MicroTasksの特徴

  • 順番が回ってきたら全てのジョブを実行

  • PromisesqueueMicrotaskMutationObserver など

MacroTasksの特徴

  • 順番が回ってきたら一つずつタスクを実行

  • setTimeoutsetIntervalrequestAnimationFrame など


▼サンプルコード

setTimeout(() => {
  console.log("MACRO task1");
});

new Promise((resolve, reject) => {
  console.log("Promise"); 
  resolve();
}).then(() => {
  console.log("micro task1");
});

console.log("global end");

// Promise
// global end
// micro task1
// MACRO task1

Promiseの中にsetTimeoutを入れたときの挙動

new Promise((resolve, reject) => {
  console.log("Promise");

  setTimeout(() => {
    console.log("MACRO task1");
    resolve();
  });

}).then(() => {
  console.log("micro task1");
});

console.log("global end");

// Promise
// global end
// MACRO task1
// micro task1

thenの中にsetTimeoutを入れたときの挙動

new Promise((resolve, reject) => {
  console.log("Promise");

  setTimeout(() => {
    console.log("MACRO task1");
    resolve();
  });
})
  .then(() => {
    console.log("micro task1");
  })
  .then(() => {
    console.log("micro task2");
    setTimeout(() => {
      console.log("MACRO task2");
    });
  })
  .then(() => {
    console.log("micro task3");
  });

console.log("global end");

// Promise
// global end
// MACRO task1
// micro task1
// micro task2
// micro task3
// MACRO task2

参考

www.udemy.com

非同期処理のPromiseを理解する

Promiseとは

コールバック地獄を解消し、非同期処理をより簡単に可読性が上がるように書けるようにしたもの。

基本的なPromise構文

new Promise((resolve, reject) => {
  console.log("promise"); // 同期処理
  resolve("resolve data"); // or reject('reject data');
})
  .then(() => {
    console.log("then"); // 非同期処理
  })
  .catch(() => {
    console.log("catch"); // 非同期処理
  })
  .finally(() => {
    console.log("finally"); // 非同期処理
  });

console.log("global end");

// promise
// global end
// then(rejectであれば、「catch」が返ってくる)
// finally

Promiseの挙動

下記のように、resolve(); や reject(); を使用しなければ、非同期処理(.then(), .catch(), .finally())は走らない

new Promise((resolve, reject) => {
  console.log("promise");
})
  .then(() => {
    console.log("then");
  })
  .catch(() => {
    console.log("catch");
  })
  .finally(() => {
    console.log("finally");
  });

console.log("global end");

// promise
// global end

データの受け渡し

resolveなら.then()の引数に、rejectなら.catch()の引数にそれぞれデータが渡る。 ただし、.finallyにはデータは渡らずundefinedになる。

new Promise((resolve, reject) => {
  console.log("promise");
  resolve("resolve data"); // or reject("reject data")
})
  .then((data) => {
    console.log("then: " + data);
  })
  .catch((data) => {
    console.log("catch: " + data);
  })
  .finally((data) => {
    console.log("finally: " + data);
  });

console.log("global end");

// promise
// global end
// then: resolve data(rejectであれば、「then: reject data」が返ってくる)
// finally: undefined

Promiseの非同期処理の実行

new Promise((resolve, reject) => {
  console.log("promise");

  // setTimeoutを利用し、2秒後にresolve()を実行
  setTimeout(() => {
    resolve("resolve data"); // or reject("reject data")
  }, 2000);
})
  .then((data) => {
    console.log("then: " + data);
  })
  .catch((data) => {
    console.log("catch: " + data);
  })
  .finally((data) => {
    console.log("finally");
  });

console.log("global end");

// promise
// global end
// (...2秒後)
// then: resolve data
// finally 

callback地獄をPromsieに書き換える

callback関数

function sleep(callback, val) {
  setTimeout(() => {
    console.log(val++);
    callback(val);
  }, 1000);
}

sleep((val) => {
  sleep((val) => {
    sleep((val) => {
      console.log("callback hell end");
    }, val);
  }, val);
}, 0);

console.log("global end");

// global end
// 0
// 1
// 2
// callback hell end

Promise構文

function sleep(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(val++);
      resolve(val);
    }, 1000);
  });
}

sleep(0)
  .then((val) => {
    return sleep(val);
  })
  .then((val) => {
    return sleep(val);
  });

console.log("global end");

// global end
// 0
// 1
// 2

全てのPromise処理が終わった後に処理を実行する(Promise.all)

sleep(0), sleep(1), sleep(2) の処理全てが完了した後に、thenが実行

function sleep(val) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(val++);
      resolve(val);
    }, val * 1000);
  });
}

Promise.all([sleep(0), sleep(1), sleep(2)]).then((val) => {
  console.log(val);
});

console.log("global end");

// global end
// 0
// 1
// 2
// [ 1, 2, 3 ]

参考

www.udemy.com

非同期処理のcallback関数を理解する

callback関数とは

callback関数は、ある関数(A関数)が実行される際に、その中で別の関数(B関数)を呼び出すこと。

console.log("start");

function timeout() { // B関数
  console.log("setTimeout");
}
setTimeout(timeout, 1000); // A関数

console.log("end");

// コンソール結果
// start
// end
// setTimeout

または下記のような記述もできる。

console.log("start");

setTimeout(() => {
  console.log("setTimeout");
}, 1000);

console.log("end");

// コンソール結果は同上

callback関数の欠点

【欠点1】callback地獄

可読性が著しく低くなる

function sleep(callback, val) {
  setTimeout(function () {
    console.log(val++);
    callback(val);
  }, 1000);
}

sleep(function (val) {
  sleep(function (val) {
    sleep(function (val) {
      sleep(function (val) {
        sleep(function (val) {
          sleep(function (val) {
            console.log("callback hell done");
          }, val);
        }, val);
      }, val);
    }, val);
  }, val);
}, 0);

【欠点2】エラー処理がうまく動かない

下記コードは、try文の中でsetTimeoutが実行されない(1秒後に実行)。

// 正常に例外処理が実行されないコード
try {
  setTimeout(() => {
    throw new Error("setTimeout error");
  }, 1000);
} catch (error) {
  console.log(`catch error: ${error}`);
}

下記のようにsetTimeoutの中に例外処理を書けば正常に動く

// 正常に例外処理が実行されるコード
setTimeout(() => {
  try {
    throw new Error("setTimeout error");
  } catch (error) {
    console.log(`catch error: ${error}`);
  }
}, 1000);

上記のような課題を解決するために、Promise オブジェクトが誕生。

参考

www.udemy.com www.udemy.com