Unlocking new capabilities for PWA

5d9cd19df0e91caac118b793b4f803d5?s=47 Takepepe
February 01, 2020

Unlocking new capabilities for PWA

5d9cd19df0e91caac118b793b4f803d5?s=128

Takepepe

February 01, 2020
Tweet

Transcript

  1. Unlocking new capabilities for PWA PWA Night CONFERENCE 2020 @Takepepe

  2. About Me ▪ Takefumi Yoshii / @Takepepe ▪ DeNA /

    DeSC Healthcare ▪ Frontend Developer ▪ TypeScript Meetup JP member 2
  3. Agenda ▪ 1. PWA や Web App で利用可能な、実験的機能について ▪ 2.

    とある実験的機能 を利用してアプリを作った話 ▪ 3. とある実験的機能 の API 概要 ▪ 4. API 取り扱いポイント・React Redux に統合する話 3
  4. 1. Web Experimental Features

  5. 1-1. Bridging the NativeApp Gap ServiceWorker のキャッシュ戦略により、 PWA のみならず、Webブラウジングに 快適な

    UX がもたらされています。 1. Web Experimental Features
  6. 1-1. Bridging the NativeApp Gap 1. Web Experimental Features その快適なレスポンスから、

    Web アプリケーション開発技術で、 Native アプリと同等機能を提供することに、 多くの期待が寄せられています。
  7. 1-1. Bridging the NativeApp Gap 1. Web Experimental Features しかし、Web

    ブラウザに許容されている 機能は限定的です。端末固有の機能に アクセスする Low-Level API の多くは、 JavaScript には解放されていません。
  8. 1-1. Bridging the NativeApp Gap 1. Web Experimental Features その状況を前進するべく、

    Google Chrome では Low-Level API への橋渡しを 積極的に行なっています。 Experimental Features(実験的機能) には、そんな機能が多数控えています。
  9. 1-2. Origin Trial 1. Web Experimental Features chrome://flags で実験的機能を有効にすれば、 その機能を試すことができます。

    そして、実験的機能を期限付き・ドメイン制限で 提供する「Origin Trial」もあります。 https://developers.chrome.com/origintrials
  10. 1-2. Origin Trial 1. Web Experimental Features 昨年 5月に開催された「Google I/O」

    昨年 11月に開催された「Chrome Dev Summit」で、 この「Origin Trial」について紹介がありました。 可能性の広がりに、期待が高まりますね!
  11. 1-2. Origin Trial 1. Web Experimental Features 今日ご紹介するのは、 現在の私たちの生活には欠かすことのできない あの技術についてです。

    最も直近に解放された API でもあります。
  12. Web NFC

  13. 2. Devlop Trial

  14. 2-1. Web NFC Media MEMO Web NFC のトライアルとして、 デモアプリを作成しました。 次の検証環境で動作確認をしています。

    ▪ Pixel 3a ▪ Android 9 ▪ Google Chrome Canary v81 2. Devlop Trial https://vimeo.com/388157513
  15. 2-1. Web NFC Media MEMO デモアプリは配信もしていますので、 NFC 機能付き Android をお持ちの方は

    試していただけると嬉しいです。 github にコードも公開しています。 https://webnfc-media-memo.netlify.com/ https://github.com/takefumi-yoshii/webnfc-media-memo 2. Devlop Trial
  16. 2-1. Web NFC Media MEMO このアプリを起動している時、 端末で検出された NFC Tag の処理は

    フォアグラウンドのブラウザに委ねられます。 2. Devlop Trial
  17. 2-1. Web NFC Media MEMO 2. Devlop Trial テキストメモ機能を利用して NFC

    Tag に書き込んだテキストは、 NFC Tag に永続化されます。
  18. 2-1. Web NFC Media MEMO 2. Devlop Trial テキストリーダー画面でタッチすると、 書き込んだ文字列が表示されます。

    2byte 文字も可能です。
  19. 2-1. Web NFC Media MEMO 2. Devlop Trial 音声メモ機能は、 録音した後に

    NFC Tag にタッチすると、 保存することができます。
  20. 2-1. Web NFC Media MEMO そして、保存済みの NFC Tag にタッチすると、 アプリ上で音声メモを自動再生します。

    2. Devlop Trial
  21. 2-1. Web NFC Media MEMO 動画データも録画・再生が可能です。 今のところ、録画の尺は設けていません。 2. Devlop Trial

  22. 2-1. Web NFC Media MEMO NFC Tag にはこの他にも、 様々なデータの保存が可能です。 (ウソです!種明かしは最後に)

    2. Devlop Trial
  23. 2-2. Prepare

  24. 2-2. Prepare 2. Devlop Trial 一番早く API を試すことが出来る方法を紹介します。 NFC 機能が搭載された

    Android端末に、 Google Chrome Canary をインストール。 chrome://flags を URL バーに入力し、 Web-NFC を有効にします。 (端末の NFC機能を有効にすることも忘れずに)
  25. 2-2. Prepare 2. Devlop Trial localhost で挙動を確認するため、 この Android 端末を

    PC に接続します。 PC の Google Chrome を起動し、 More tools > Remote devices から、 接続している該当端末を選択。
  26. 2-2. Prepare 2. Devlop Trial Port forwarding に、開発サーバーの localhost を指定。

    モバイル端末の Developer Console にアクセスします。
  27. 2-2. Prepare Developer Console から、Web NFC が利用可能かを確認します。 定義が存在すれば、Web NFC を試す環境が整いました。

    2. Devlop Trial if ('NDEFReader' in window) { /* ... Scan NDEF Tags */ } if ('NDEFWriter' in window) { /* ... Write NDEF Tags */ }
  28. 3. API Overview

  29. 3-1. About NFC Tag

  30. 3-1. About NFC Tag API 詳細の話の前に、 NFC Tag について少し紹介します。 NFC

    Tag には様々な種類があります。 3. API Overview
  31. 3-1. About NFC Tag Suica / Edy などに台頭する電子マネーは、 NFC-F(Type-F)にあたります。 データ管理やセキュリティ機能が

    強化されているものです。 3. API Overview
  32. 3-1. About NFC Tag NFC-F については、今回調べたなかでは、 Web NFC で読み書きはできませんでした。 (ただし

    検出は可能) 3. API Overview
  33. 3-1. About NFC Tag Origin Trials の description を引用します。 Low-level

    I/O operation などは、対応未定とのこと。 3. API Overview Low-level I/O operations (e.g. ISO-DEP, NFC-A/B, NFC-F) and Host-based Card Emulation (HCE) are not supported within the current scope.
  34. 3-1. About NFC Tag 現状試すことができる API は限定的ですが、 NFC Tag を選べば、デモアプリの様なことが可能です。

    3. API Overview Low-level I/O operations (e.g. ISO-DEP, NFC-A/B, NFC-F) and Host-based Card Emulation (HCE) are not supported within the current scope.
  35. 3-1. About NFC Tag デモアプリで利用想定しているのは、 NFC-A(Type-A)のものです。 これであれば「Web NFC」でも 読み書きをすることが可能です。 カードタイプ・シールタイプなどがあります。

    3. API Overview 例(カードタイプ / ¥600 / 12枚、シールタイプ / ¥790 / 11枚)
  36. 3-1. About NFC Tag NPX 社の「NTAG 215 チップ(NFC-A )」 が採用された

    NFC Tag は多く流通しており、 安価に入手することができます。 3. API Overview 例(カードタイプ / ¥600 / 12枚、シールタイプ / ¥790 / 11枚)
  37. 3-1. About NFC Tag 形だけでなく、容量も様々なものがあります。 そんなに大容量ではありません。 NTAG 213: 144Byte NTAG

    215: 504Byte NTAG 216: 888Byte 3. API Overview 例(カードタイプ / ¥600 / 12枚、シールタイプ / ¥790 / 11枚)
  38. 3-1. About NFC Tag 読み込み可能な NFC Tag の仕様について、 より詳細な情報は以下を参照しましょう。 https://w3c.github.io/web-nfc/#x4-1-ndef-compatible-tag-types

    3. API Overview
  39. 3-2. NDEFReader

  40. 3-2. NDEFReader 簡単な読み込み例を確認していきましょう。 3. API Overview const reader = new

    NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  41. NDEFReader が NFC 読み込みに必要なインスタンスです。 scan 関数は Promise を返します。 3-2. NDEFReader

    3. API Overview const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  42. 3-2. NDEFReader onreading ハンドラの callback は NDEFReadingEvent を受け取ります。 3. API

    Overview const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  43. 3-2. NDEFReader serialNumber は、物理タグ出荷時に 付与されている一意の ID です。 3. API Overview

    const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  44. 3-2. NDEFReader serialNumber を判別することで 「一意の NFC Tag を読み込んだ」ことを判別することができます。 3. API

    Overview const reader = new NDEFReader() reader.scan().then(() => { console.log("Scan started successfully.") reader.onreading = event => { console.log(`NDEF message read.${event.serialNumber}`) } }).catch(error => { console.log(`Error! Scan failed to start: ${error}.`) })
  45. 3-3. NDEFWriter

  46. 3-3. NDEFWriter 書き込みもいたって簡単です。 文字列を NFC Tag に書き込んでみます。 3. API Overview

    const writer = new NDEFWriter() writer.write("Hello World").then(() => { console.log("Message written.") }).catch(error => { console.log(`Write failed :-( try again: ${error}.`) })
  47. 3-3. NDEFWriter 書き込みには、 文字列を渡すだけの方法があります。 3. API Overview const writer =

    new NDEFWriter() writer.write("Hello World").then(() => { console.log("Message written.") }).catch(error => { console.log(`Write failed :-( try again: ${error}.`) })
  48. 3-3. NDEFWriter こちらの戻り値も Promise です。 NFC Tag が検出されたタイミングで、書き込みを試みます。 3. API

    Overview const writer = new NDEFWriter() writer.write("Hello World").then(() => { console.log("Message written.") }).catch(error => { console.log(`Write failed :-( try again: ${error}.`) })
  49. 3-3. NDEFWriter 書き込むのは文字列の他に、 NDEFMessage オブジェクトを指定する方法があります。 3. API Overview const message:

    NDEFMessage = { records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }] } const writer = new NDEFWriter() writer.write(message).then(() => { console.log("Message written.") }).catch(_ => { console.log("Write failed :-( try again.") })
  50. 3-3. NDEFWriter 書き込むのは文字列の他に、 NDEFMessage オブジェクトを指定する方法があります。 3. API Overview const message:

    NDEFMessage = { records: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }] } const writer = new NDEFWriter() writer.write(message).then(() => { console.log("Message written.") }).catch(_ => { console.log("Write failed :-( try again.") })
  51. 3-4. NDEFMessage

  52. 3-4. NDEFMessage NFC Tag の読み込み時にも、NDEFMessage を受け取ります。 この内容を読み取ってみます。 3. API Overview

    function consoleNDEFRecords(message: NDEFMEssage) { if (message.records.length === 0) return message.records.map(record => { const decoder = new TextDecoder() console.log(`Text: ${decoder.decode(record.data)}`) }) }
  53. 3-4. NDEFMessage NFC Tag に書き込まれた文字列は、直接読み取ることができません。 TextDecoder インスタンスを利用し、デコードをする必要があります。 3. API Overview

    function consoleNDEFRecords(message: NDEFMEssage) { if (message.records.length === 0) return message.records.map(record => { const decoder = new TextDecoder() console.log(`Text: ${decoder.decode(record.data)}`) }) }
  54. 3-4. NDEFMessage 1byte文字列であれば、このままでも問題ありませんが、 2byte文字を含む文字列の場合、encoding の指定が必要です。 3. API Overview function consoleNDEFRecords(message:

    NDEFMEssage) { if (message.records.length === 0) return message.records.map(record => { const decoder = new TextDecoder(record.encoding) console.log(`Text: ${decoder.decode(record.data)}`) }) }
  55. 3-4. NDEFMessage この様に、NFC-A の NFC Tag であれば、 簡単に読み書き可能であることがわかります。 3. API

    Overview function consoleNDEFRecords(message: NDEFMEssage) { if (message.records.length === 0) return message.records.map(record => { const decoder = new TextDecoder(record.encoding) console.log(`Text: ${decoder.decode(record.data)}`) }) }
  56. 3-5. NDEFRecord

  57. 3-5. NDEFRecord NDEFRecord について、もう少し詳しくみていきましょう。 Web IDL interface はつぎの様に定義されています。 3. API

    Overview interface NDEFRecord { constructor(NDEFRecordInit recordInit) readonly attribute USVString recordType readonly attribute USVString? mediaType readonly attribute USVString? id readonly attribute DataView? data readonly attribute USVString? encoding readonly attribute USVString? lang sequence<NDEFRecord>? toRecords() }
  58. 3-5. NDEFRecord NDEFRecord のなかで必須プロパティであるのは、recordType です。 NDEFRecord の解析はこれを確認するところから始まります。 3. API Overview

    interface NDEFRecord { constructor(NDEFRecordInit recordInit) readonly attribute USVString recordType readonly attribute USVString? mediaType readonly attribute USVString? id readonly attribute DataView? data readonly attribute USVString? encoding readonly attribute USVString? lang sequence<NDEFRecord>? toRecords() }
  59. 3-5. NDEFRecord recordType は任意の文字列と することができますが、 Well-known type records として取り決められている のは次の3種類です。

    ▪ text ▪ url ▪ smart-poster 3. API Overview
  60. 3-5. NDEFRecord recordType: "url" の record を 保持した NFC Tag

    を検出した 端末は、何もアプリケーション を起動していない場合、 ブラウザの起動を促します。 3. API Overview
  61. 3-5. NDEFRecord そのため、Web NFC を利用し recordType: "url" を指定した NDEFRecord を書き込めば、

    任意の NFC Tag を ブラウザランチャーと することもできます。 3. API Overview
  62. 3-5. NDEFRecord recordType に アプリケーション特有の 接頭辞を付与し、検出した タグの処理を絞り込む例も 紹介されています。 ▪ https://w3c.github.io/web-nfc/

    3. API Overview
  63. 4. Handling APIs in App

  64. 4-1. Permission Request

  65. 4-1. Permission Request Web NFC を使うアプリは、 何よりもはじめにパーミッションを 得る必要があります。 4. Handling

    APIs in App const reader = new NDEFReader() await reader.scan()
  66. 4-1. Permission Request 注意しなければいけないのは、 ページロードと同時に API をコールしないことです。 4. Handling APIs

    in App const reader = new NDEFReader() await reader.scan()
  67. 4-1. Permission Request このタイミングでコールすると、 パーミッション・プロンプトが 表示されません。 4. Handling APIs in

    App const reader = new NDEFReader() await reader.scan()
  68. 4-1. Permission Request パーミッション・プロンプトを 表示させるため、押下コールバックで API をコールする設計にしましょう。 4. Handling APIs

    in App const reader = new NDEFReader() await reader.scan()
  69. 4-1. Permission Request そして、NDEFReader インスタンスを 解放しない留意が必要です。scan が実行 されていないと、別アプリが検出に反応し、 フォアグラウンドを奪われてしまいます。 4.

    Handling APIs in App const reader = new NDEFReader() await reader.scan()
  70. 4-1. Permission Request そもそも端末設定で、NFC 機能への アクセスを拒否している場合もあります。 それらの考慮と併せ、利用できないケースを ユーザーに明示しましょう。 4. Handling

    APIs in App const reader = new NDEFReader() await reader.scan()
  71. 4-2. Permission Handling

  72. 4-2. Permission Handling サンプルアプリでは カメラ・マイクを利用しており、 こちらもパーミッションが必要になります。 下記関数を利用することで MediaStream が立ち上がりますが、 「カメラ・マイク」のパーミッションプロンプトも同時に表示されます。

    4. Handling APIs in App function getUserMedia(constraints: MediaStreamConstraints) { return navigator.mediaDevices.getUserMedia(constraints) }
  73. 4-2. Permission Handling 4. Handling APIs in App アクセス権限状態の識別は重要です。 一度でも機能へのアクセスをブロックすると、

    それ以降、その機能にアクセスすることができません。 (設定解除への誘導が必要) function getUserMedia(constraints: MediaStreamConstraints) { return navigator.mediaDevices.getUserMedia(constraints) }
  74. 4-2. Permission Handling サンプルアプリでは、Permission API を利用し、 Origin の権限状態を取得しています。 ブロックされている状況などを把握するために、 Permission

    API は有効です。 4. Handling APIs in App const status = await navigator.permissions.query({ name: 'nfc' }) console.log(status.state) // "granted" | "denied" | "prompt" status.onchange = () => { // dosomething ex:) re render view }
  75. 4-2. Permission Handling サンプルアプリでは、Permission API を利用し、 Origin の権限状態を取得しています。 ブロックされている状況などを把握するために、 Permission

    API は有効です。 4. Handling APIs in App const status = await navigator.permissions.query({ name: 'nfc' }) console.log(status.state) // "granted" | "denied" | "prompt" status.onchange = () => { // dosomething ex:) re render view }
  76. 4-2. Permission Handling query 関数で状態を知る方法と同じ様に、 request 関数でパーミッション・プロンプトを表示することもできます。 いずれも、ブラウザによってサポート状況がまちまちなので 利用する前に確認しましょう。 4.

    Handling APIs in App const status = await navigator.permissions.query({ name: 'nfc' }) console.log(status.state) // "granted" | "denied" | "prompt" status.onchange = () => { // dosomething ex:) re render view }
  77. 4-2. Permission Handling 権限状況をユーザーに知らせる View があると親切です。 権限変更された時に発火されるハンドラも指定できます。 Native API の多くは、Permission

    を求めるものが多いです。 利用するアプリケーションでは、手厚くサポートしていきましょう。 4. Handling APIs in App const status = await navigator.permissions.query({ name: 'nfc' }) console.log(status.state) // "granted" | "denied" | "prompt" status.onchange = () => { // dosomething ex:) re render view }
  78. 4-3. Media Recorder

  79. 4-3. Media Recorder 4. Handling APIs in App メディアの記録は、MediaRecorder を利用しています。

    録音・録画がとても簡単に実現できます。 const mediaRecorder = new MediaRecorder(stream, options) mediaRecorder.onstart = () => { console.log('start rec') } mediaRecorder.onstop = () => { console.log('stop rec') } mediaRecorder.ondataavailable = event => { console.log('data available') }
  80. 4-3. Media Recorder メディアの記録に必要な MediaStream を立ち上げた後に、 MediaRecorder インスタンスを生成します。 4. Handling

    APIs in App const mediaRecorder = new MediaRecorder(stream, options) mediaRecorder.onstart = () => { console.log('start rec') } mediaRecorder.onstop = () => { console.log('stop rec') } mediaRecorder.ondataavailable = event => { console.log('data available') }
  81. 4-3. Media Recorder 「ondataavailable」ハンドラでは、録画データを受け取ることができます。 この録画データを、任意の方法で保存します。 4. Handling APIs in App

    const mediaRecorder = new MediaRecorder(stream, options) mediaRecorder.onstart = () => { console.log('start rec') } mediaRecorder.onstop = () => { console.log('stop rec') } mediaRecorder.ondataavailable = event => { console.log('data available') }
  82. 4-4. SerialNumber & IndexedDB

  83. 4-4. SerialNumber & IndexedDB デモアプリでは、NFC Tag にメディアの記録をしているかの様に、 演出を施していました。 これは種明かしをすると、NFC Tag

    の「serialNumber」に紐付け、 データを永続化しているだけです。 4. Handling APIs in App localforageStore.getItem(id) localforageStore.setItem(id, blob)
  84. 4-4. SerialNumber & IndexedDB 4. Handling APIs in App その一意の

    ID を key に、IndexedDB に メディア SRC である Blob を書き込んでいます。 デモアプリでは、localforage を利用しています。 IndexedDB のラッパーライブラリです。 localforageStore.getItem(id) localforageStore.setItem(id, blob)
  85. 4-4. SerialNumber & IndexedDB IndexedDB の取り扱いはやや煩雑です。 localStorage と同じ感覚で使えるこちらのライブラリ。 Promise を返してくれるため、設計に盛り込みやすいです。

    (今回はスピード重視で選定しました) 4. Handling APIs in App localforageStore.getItem(id).then(...) localforageStore.setItem(id, blob).then(...)
  86. 4-5. Side Effect Handling

  87. 4-5. Side Effect Handling さて、ここまでで紹介した複数の API。 コールバックハンドラや Promise がほとんどで、 非同期処理のサラダボウルですね。

    ひとつひとつの使い方が単純でも、順序や制御など、 アプリケーションへの統合に一工夫が必要になります。 4. Handling APIs in App
  88. 4-5. Side Effect Handling 4. Handling APIs in App 副作用が混在する

    Application は、 副作用に特化したライブラリが便利です。 私が手に馴染んでいる React では、 今回の様な アプリ色が濃いものの場合、 Redux と redux-saga を使っています。
  89. 4-5. Side Effect Handling 4. Handling APIs in App Store

    構成は「共有機能ドメイン」と「各ページドメイン」 に分けて、Reducer や Action をそれぞれ設けています。 これらのドメインをより集め、ひとつの Store とします。 各ページドメインは「共有機能ドメインの利用者」という位置付けです。 この構成により、各々の実装が単純になっています。
  90. 4-5. Side Effect Handling MediaRecorder を例に見てみましょう。 録画・録音の責務は、機能ドメインで一限管理します。 4. Handling APIs

    in App mediaRecorder.onstart = () => { store.dispatch(creators.onStartRecording()) } mediaRecorder.onstop = () => { store.dispatch(creators.onStopRecord()) } mediaRecorder.ondataavailable = event => { const blob = new Blob([event.data], { type: 'video/webm' }) store.dispatch(creators.onDataAvailable(blob)) }
  91. 4-5. Side Effect Handling 4. Handling APIs in App JavaScript

    Native API コールバックハンドラの中で、 Redux Store の dispatch を実行しています。 mediaRecorder.onstart = () => { store.dispatch(creators.onStartRecord()) } mediaRecorder.onstop = () => { store.dispatch(creators.onStopRecord()) } mediaRecorder.ondataavailable = event => { const blob = new Blob([event.data], { type: 'video/webm' }) store.dispatch(creators.onDataAvailable(blob)) }
  92. 4-5. Side Effect Handling 各ページは、共有機能ドメインの Action を購読したり、 共有機能ドメイン に Action

    を発行します。 4. Handling APIs in App switch (action.type) { case PermanentStorageTypes.ON_SUCCESS_PUT: return handleStateByMode(state, 'ready') case MediaRecorderTypes.ON_START_RECORDING: return handleStateByMode(state, 'recording') case MediaRecorderTypes.ON_DATA_AVAILABLE: return { ...state, blob: action.payload.blob } default: return state } Page Reducer Subscribe Actions
  93. 4-5. Side Effect Handling 各ページは、共有機能ドメインの Action を購読したり、 共有機能ドメイン に Action

    を発行します。 4. Handling APIs in App const handleClickIcon = React.useCallback(() => { switch (mode) { case 'ready': dispatch(startRecording({ audio: true, video: true })) break case 'recording': dispatch(stopRecording()) } }, [mode]) Page Component Dispatch Actions
  94. 4-5. Side Effect Handling 今回のデモアプリで redux-saga が最も役にたったシーンは、 メディアの録画開始時に表示されるカウントダウン機能です。 これは演出以外にも、重要な役割を担っています。 カメラの起動直後(MediaStream

    開始直後)暗い場所などでは 露出補正のため数秒間暗くなってしまう瞬間があります。 このタイミングで録画を始めてしまうと、良い画が撮れません。 4. Handling APIs in App
  95. 4-5. Side Effect Handling カウントダウンも一種の副作用であり、非同期処理といえます。 カウントダウン中に生じる別の副作用(画面遷移など)が問題になり得ます。 4. Handling APIs in

    App let count = 4 while (count) { const [normal, abnormal] = yield race([ call(countDownStep), call(countDownCancel) ]) if (abnormal !== undefined) break count -= normal yield put(creators.onCountDown(count)) }
  96. 4-5. Side Effect Handling この様な問題も saga-effect の組み合わせで克服することができます。 イレギュラーなケースもカバーしてくれる、心強い味方です。 4. Handling

    APIs in App let count = 4 while (count) { const [normal, abnormal] = yield race([ call(countDownStep), call(countDownCancel) ]) if (abnormal !== undefined) break count -= normal yield put(creators.onCountDown(count)) }
  97. まとめ

  98. 機能が複雑・複合的になると、 非同期処理が入り乱れることが分かりました。 Redux・redux-saga の例を紹介しましたが、 非同期処理に強いライブラリは様々なものがありますので、 手に馴染むものを見つけてみてください。 まとめ

  99. まとめ Google Chrome の Experimental Features は、 面白いものが盛りだくさんです。 Native API

    にどんどんリーチできる様になっています。 アイディア次第で「便利・おもしろい」 アウトプットができそうです。
  100. まとめ Experimental Features のトライアルが、 新しいサービスの種になるかもしれません。 ぜひ、挑戦してみてください。

  101. ご静聴ありがとうございました