Slide 1

Slide 1 text

YouTube Live (2020.11.04 Thur. 21:00~)

Slide 2

Slide 2 text

話す人 現役のエンジニア二人 赤貝が好きな CTO と デザイン勉強中のエンジニア @mu_vpoe 最近の仕事は figma で画 面設計をつくることで す。英語の勉強してる。 ムー zaru @zaru CTO, Love 赤貝, JavaScript, Firebase, Web Components. Twitter フォロー お願いします!

Slide 3

Slide 3 text

非同期処理は簡単 async, await を使えば 本当か なぁ? 嘘でしょ …かも?

Slide 4

Slide 4 text

await で非同期の完了を待つ const res = await fetch('http://localhost:3000/'); JavaScript 非同期処理 完了を待つよ、という指示 非同期な処理の前に await と置くことで、同期的にコードを書く ことができて便利! という、とても簡単で便利な感じですね!! ???

Slide 5

Slide 5 text

非同期 って何?

Slide 6

Slide 6 text

同期と非同期処理の違い syncFuncA() syncFuncB() syncFuncC() 同期処理の流れ asyncFuncA() syncFuncB() syncFuncC() 非同期処理の流れ // A が終わってから B // B が終わってから C // A を待たずに B // B が終わってから C 裏で処理をして いるイメージ 面倒くさ いジャン 非同期 嫌だ // A がいつ終わるかは // 分からない プログラマ的には、書いたコード順に実行して行って欲 しい(同期的)。非同期な処理が入ると、結果を受け 取って次の処理をするためのコードを書くのが面倒。

Slide 7

Slide 7 text

非同期 必要ない んじゃ?

Slide 8

Slide 8 text

ブラウザはとにかく忙しい マウス移動 ボタンクリック 文字選択 フォーム入力 HTML レンダリ ング CSS スタ イル描画 ユーザ操 作へ反応 ブラウザは HTML の表示に加えて、ユーザ操作に対する反応をし なくてはならず、絶え間なく忙しい。忙しいけど頑張っている。 サーバとの 通信

Slide 9

Slide 9 text

時間のかかる処理があると固まる めちゃく ちゃ重い 処理 時間のかかる処理があると、ブラウザは他の作業ができな くなる。ユーザの操作も受け付けることができない。ブラ ウザも頑張っているが、仕方がない…。 ・・・ ブラウザが固まって ユーザが操作不能に ❌ (*) なぜ他の処理ができないのかというとシングルスレッドだからという ことなのだけど、突っ込んだ話になるので別の機会に…

Slide 10

Slide 10 text

固まるデモ https://gist.github.com/zaru/b8275a59305eff78560f8a90b51c1e4b

Slide 11

Slide 11 text

時間のかかる処理があっても待たない めちゃく ちゃ重い 処理 あとで そのすきに色んな 操作を受け付ける ことができる ざっくり言うと、時間のかかる処理が終わるのを待たず に、他の処理を受け付けられるようにする => 非同期処理 非同期 必要だ

Slide 12

Slide 12 text

非同期処理の結果を受け取る

Slide 13

Slide 13 text

通信処理() もし同期処理だったら リクエスト レスポンス この間、他の 操作や処理は 何もできない サーバ ❌ 通信処理() 通信は非同期 リクエスト レスポンス 通信中、他の 操作を受け付 ける サーバ おさらい

Slide 14

Slide 14 text

通信処理() もし同期処理だったら リクエスト レスポンス この間、他の 操作や処理は 何もできない サーバ ❌ 通信処理() 通信は非同期 リクエスト レスポンス 通信中、他の 操作を受け付 ける サーバ つまり非 同期にし たいんだ ね

Slide 15

Slide 15 text

通信処理() 通信中、他の 操作を受け付 ける 通信は非同期 リクエスト レスポンス サーバ const res = 同期_通信処理('http://localhost:3000/'); // res 変数には通信処理の結果が入って来るので、すぐ使える JavaScript 通信処理() もし同期処理だったら リクエスト レスポンス この間、他の 操作や処理は 何もできない サーバ ❌ シンプルだ けど操作で きないのは 困る

Slide 16

Slide 16 text

通信処理() もし同期処理だったら リクエスト レスポンス この間、他の 操作や処理は 何もできない サーバ ❌ 通信処理() 通信中、他の 操作を受け付 ける 通信は非同期 リクエスト レスポンス サーバ const res = 非同期_通信処理('http://localhost:3000/'); // res 変数には通信処理の結果は入ってこない!! JavaScript 困る 結果が使えないと意味ない…!

Slide 17

Slide 17 text

- コールバック関数 - Promise ~ then - Promise ~ await 結果を受け取る3つの方法 Promise ...?

Slide 18

Slide 18 text

コールバック関数で受け取る 昔から使われている XMLHttpRequest で結果を受け取るには load のイベ ントリスナーに関数を登録しておく。非同期処理の結果を受け取るには、こ のようなイベントやコールバック関数でやるのが昔の JS 流儀。 問題は、非同期処理の書き方が統一されていないことと、連続した非同期処 理をすると深いネスト構造になり破綻しやすいこと。 const xhr = new XMLHttpRequest(); xhr.addEventListener('load', (response) => { console.log(response.target.responseText); }); xhr.open('GET', 'http://localhost:3000/'); xhr.send(); XMLHttpRequest コールバック関数

Slide 19

Slide 19 text

波動拳 a.k.a Callback Hell fs.readdir(source, function (err, files) { if (err) { console.log('Error finding files: ' + err) } else { files.forEach(function (filename, fileIndex) { console.log(filename) gm(source + filename).size(function (err, values) { if (err) { console.log('Error identifying file size: ' + err) } else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function (width, widthIndex) { height = Math.round(width / aspect) console.log('resizing ' + filename + 'to ' + height + 'x' + height) this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err) }) }.bind(this)) } }) }) } }) http://callbackhell.com/

Slide 20

Slide 20 text

コールバック関数 function callback(data) { console.log(data); } function something(id, callback) { const result = `ID: ${id}`; callback(result) } something('zaru', callback); JS 初学者向け 通称、コールバック関数 特別な機能があるわけで はなく単なる関数定義 引数に関数自体を渡すことができる ここでは関数は実行されない点に注意 引数の関数に、処理した結果を渡し た上で実行する こうすることで特定の処理結果を任 意の関数に任せることができて便利 1 2 3

Slide 21

Slide 21 text

Promise の登場 ES2015 からの新機能

Slide 22

Slide 22 text

Promise が解決すること - 非同期処理の標準化(どんな処理も同じ書き方に統一される) - 連続した非同期処理を簡単にかける - 非同期処理に関するいろいろな操作方法の提供 つまり 良い感じ のやつ

Slide 23

Slide 23 text

Promise は3つの状態を持つ Pending Fulfilled Rejected 最初は Pending (待機) で、非同期処理が正常に終わったら Fulfilled (満足) へ、なんらかの理由で失敗した場合は Rejected (拒否) 状態に移る。 const promise = asyncFunc(); // Promise{} promise.then((response) => { // Promise{} }).catch((error) => { // Promise{} }); Promise の基本構文 正常に終わる場合は then へ 失敗した場合は catch へ

Slide 24

Slide 24 text

Promise は then で受け取る fetch は XMLHttpRequest と同じように通信ができる。違う点は Promise というオブジェクトを使って非同期処理をする所。 Promise はコールバック関数とは違い、標準化され使い方が統一されてい る。then でつないでいくことで、連続した非同期処理をネストさせずに1直 線で書くことができるのがメリット。 fetch('http://localhost:3000/') .then((response) => { return response.text(); }) .then((response) => { return response; }); fetch then の中で受け取れる 次の非同期処理もつなげられる

Slide 25

Slide 25 text

await の登場 ES2017 からの新機能

Slide 26

Slide 26 text

await はその場で待つ await を使うと then を使わずに、その場で待ってくれる。つまり、コード の書き方としては同期的に書けるが、実際の処理は非同期で行うので、他の 処理を止めることはない、いいとこ取りな機能。 const response = await fetch('http://localhost:3000/'); const text = await response.text(); console.log(text); fetch スッキリ

Slide 27

Slide 27 text

await 拒否の受け取り方 const response = await fetch('http://localhost/').catch((error) => { console.log(error); }); try { const response = await fetch('http://localhost/') } catch (error) { console.log(error); } ドットチェイン catch で 受け取ることができる try の catch でも受け取れる どちらで書いた方がいいかはケースバイケースだが、ドッ トチェインの catch で受け取った方がシンプルな書き方 になることが多い。

Slide 28

Slide 28 text

await 便利〜 つまり Promise な非同期処理の結果 を受け取るだけなら await 付けておけ ば OK

Slide 29

Slide 29 text

Slide 30

Slide 30 text

asyncは?

Slide 31

Slide 31 text

asyncの使い方 ES2017 からの新機能

Slide 32

Slide 32 text

async は非同期関数を作る async function asyncFunc() { return 1; } function asyncPromise(){ return new Promise((resolve) => resolve(1)) } asyncPromise().then((res) => res ); asyncFunc().then((res) => res ); Promise と async 関数の前に置くことで自動で Promise を返す非同期関数になる 手動で Promise オブジェクトを 作って返す関数と基本的には同じ 呼び出し方も then や await など で同じように使える 非同期処理を使うだけなら async は Promise を手動で作るのと 同じなんだ〜くらいの認識で OK

Slide 33

Slide 33 text

await は async 内じゃないと使えない ❌ 実はこれはエラーになる const response = await fetch('http://localhost/'); ✅ async 関数の中じゃないと await は使えない async function fetchUrl() { const response = await fetch('http://localhost/'); } (async () => { const response = await fetch('http://localhost/'); })(); async 2020.11 現在では await はトップレベルでは使えず async で非同期宣言を した関数の中でしか使えない。将来的には使えるようになる予定(NodeJS は v14.8 以降使える) https://github.com/tc39/proposal-top-level-await

Slide 34

Slide 34 text

簡単だね 本当か?

Slide 35

Slide 35 text

JS の実行フローについて おまけ

Slide 36

Slide 36 text

イベントループ タスク タスク タスク タスク タスク ユーザの操作やスクリプトの 実行などタスクを積んでいく 暇になったら古いタスクから 拾って順次処理をしていく JavaScript の処理は、イベントループと言われる構造で実行し ている。処理したいタスクをキューに積んでいき、順次処理をし ていくイメージ。

Slide 37

Slide 37 text

どの順序で出力される? console.log('start'); setTimeout(() => console.log('setTimeout'), 0); (async () => { await 0; console.log('async') })(); function promiseFunc() { return new Promise((resolve) => resolve()) } promiseFunc().then(() => console.log('promise')); Promise.resolve().then(() => console.log('resolve')); console.log('end'); JavaScript

Slide 38

Slide 38 text

どの順序で出力される? console.log('start'); setTimeout(() => console.log('setTimeout'), 0); (async () => { await 0; console.log('async') })(); function promiseFunc() { return new Promise((resolve) => resolve()) } promiseFunc().then(() => console.log('promise')); Promise.resolve().then(() => console.log('resolve')); console.log('end'); JavaScript start end async promise resolve setTimeout 答え

Slide 39

Slide 39 text

タスク タスク タスク タスク タスク タスク タスク タスク macrotask microtask レンダリング requestAnimationFrame 描画系の処理 Promise setTimeout I/O Network Mouse Keyboard 1 macrotask から1個タ スクを受け取り処理をす る 3まで完了したら次のタ スクを受け取り処理をす る 2 microtask から全て のタスクを受け取り処 理をする 3 必要があれば描画に関連する処理を行う 無限ループ

Slide 40

Slide 40 text

ありがとうございました! 次回は... 未定! 質問感想など呟いていただけると嬉しいです! - ハッシュタグ #mu_zaru - ツイッター情報 @mu_vpoe , @zaru チャンネル登録 Good ボタン お願いします! ムーザルちゃんねる