Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unlocking new capabilities for PWA

Takepepe
February 01, 2020

Unlocking new capabilities for PWA

Takepepe

February 01, 2020
Tweet

More Decks by Takepepe

Other Decks in Technology

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. ご静聴ありがとうございました