web workerで○○する話

39
Web Worker で ででで 〇〇 Niigata.js #1

Upload: ushiboy

Post on 26-Jan-2017

636 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Web Workerで○○する話

Web Workerで〇〇する話Niigata.js #1

Page 2: Web Workerで○○する話

自己紹介

ushiboy

プログラマ

SPAアプリ開発が多め

使う言語は JavaScript 8割、 pythonが 2割みたいな感じ

Page 3: Web Workerで○○する話

Web Worker ご存知ですか or おぼえてますか

Page 4: Web Workerで○○する話

Web Workerについて三行で

Webブラウザにマルチスレッドをもたらした。

Worker内だけの独自コンテキストをもつ。

Workerの外部とはメッセージングでやり取り。

Page 5: Web Workerで○○する話

ちょっと振り返り( JavaScriptの同期処理)

基本的にメインスレッド( UIスレッド)だけなので、注意が必要だった。

Page 6: Web Workerで○○する話

例 1 Busyなループfunction loopSync() { for (var i = 0; i < 1000000000; i++) { // busyなループ } console.log('finish!');}

document.getElementById('btn').addEventListener('click', function(evt) { loopSync(); // これが終わるまで何もかも待たされる}, false);

Page 7: Web Workerで○○する話

例 2 同期な HTTP通信document.getElementById('btn').addEventListener('click', function(evt) {

var xhr = new XMLHttpRequest(); xhr.open('GET', '/api/slow', false); // 第 3引数の asyncフラグをオフにして "同期 "通信に xhr.send(null); // レスポンスくるまで待たされる console.log(xhr.responseText);

}, false);

Page 8: Web Workerで○○する話

そして出てくるダイアログ

※ブラウザに寄ります

Page 9: Web Workerで○○する話

なので

ふつうは非同期な APIを使う。

setTimeout駆使したり

XMLHttpRequestは非同期で使う

Page 10: Web Workerで○○する話

Workerを使うと ...

function loopSync() { for (var i = 0; i < 1000000000; i++) { // busyなループ } console.log('finish!');}loopSync();

document.getElementById('btn').addEventListener('click', function(evt) { new Worker('worker.js');}, false);

重い部分を worker用にスクリプトごと分離

Workerを作って使う

Page 11: Web Workerで○○する話

これによって、ブラウザが怒らない

※繰り返しますが、ブラウザに寄ります

Page 12: Web Workerで○○する話

もうちょっと詳しく見ていきます

というわけで ...

Page 13: Web Workerで○○する話

Workerを使うためには

ワーカー用のスクリプトの URI渡してインスタンスを生成する

var worker = new Worker('worker.js');

このときの URIは同一生成ポリシーに従う必要がある。

Page 14: Web Workerで○○する話

メッセージのやりとり

Worker

UI

postMessageで送る。

onmessageで受け取る。

worker.postMessage('start');

worker.onmessage = function(evt) { // MessageEvent console.log(evt.data);};

Page 15: Web Workerで○○する話

(もうちょっと)メッセージのやりとり

Worker

UI

postMessageで送るデータは共有ではなくコピーされる。

onmessageで受けとったものはコピー。

worker.postMessage({ count: 1000 });

worker.onmessage = function(evt) { // MessageEvent console.log(evt.data);};

同一のインスタンスを共有せずに双方で複製。

Page 16: Web Workerで○○する話

(番外)メッセージのやりとり

postMessageで送るデータは共有ではなくコピーされる。

巨大なデータを送る場合にパフォーマンスを良くするため、

所有権の譲渡( Transferable Objects)という仕組みがある。

が、今回は省略 ...

参考https://developer.mozilla.org/ja/docs/Web/Guide/Performance/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)

Page 17: Web Workerで○○する話

Workerの中selfがグローバルスコープショートカット( UI側の window)

windowや documentなど触れないものがある

詳しくは https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers

外部スクリプトは importScriptsで読み込む

WorkerがさらにWorkerを生成とかもできる。self.onmessage = function(evt) { if (evt.data === 'start') { self.postMessage('fin'); } else if (evt.data === 'stop') { self.close(); }};

importScripts(‘foo.js’, ‘hoge.js’);

Page 18: Web Workerで○○する話

Workerの止め方

terminate呼ぶ。

ワーカー自身が内部から close。

worker.terminate();

self.close();

Page 19: Web Workerで○○する話

Worker内でのエラー

ErrorEventになるので onerrorで拾える。

worker.onerror = function(evt) { // ErrorEvent console.log(evt.message); // “Uncaught Error:/ (^o^) ”\ console.log(evt.lineno); // 12};

Page 20: Web Workerで○○する話

Workerの種類

Dedicated Worker : 今まで見てきたやつ

Shared Worker : 複数のスクリプトから共有できるやつ

Service Worker : オフラインアプリにするときに役に立つやつ

Chrome Worker : Firefox限定(アドオン用?)

Audio Worker : オーディオ処理用(らしい)

Page 21: Web Workerで○○する話

ブラウザの対応状況(デスクトップ系)

細かい機能レベルになるともっと分かれる模様

参考 https://developer.mozilla.org/ja/docs/Web/Guide/Performance/Using_web_workers#Browser_compatibility

Internet Explorer 10.0+

Firefox 3.5+

Chrome 4+

Opera 10.6+

Safari 4+

Page 22: Web Workerで○○する話

ちなみに polyfillがあるっぽい

が、そもそも polyfillでどうにかなるものではない ...

参考 : https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills#web-workers

ホントそれ

Page 23: Web Workerで○○する話

Web Worker使ってますか?

Page 24: Web Workerで○○する話

ちなみに私は ...

Page 25: Web Workerで○○する話

まったく使ってない\ (^o^)/

自分の対象範囲だと重い処理やるケースない ...

サーバーと JSON …でお話するのがほとんど

…このままではタイトル詐欺になってしまう

Page 26: Web Workerで○○する話

というわけで、ここから本題

Page 27: Web Workerで○○する話

ここでもう一度図をみてみる

じーっと見ていると ...

Worker

UI

Page 28: Web Workerで○○する話

Fluxと似てますね

※強引な気がするのは気のせいです。

Page 29: Web Workerで○○する話

というわけで

この辺りをまるっとワーカーに入れてみる

Page 30: Web Workerで○○する話

Web Workerで(雑に) Fluxするお題:カウンターアプリ

Page 31: Web Workerで○○する話

WARNING!!!!!

ここからコードが EcmaScript2015になります

Page 32: Web Workerで○○する話

UI側

function Counter(props) { const { count, plus, minus } = props; return ( <div> <div>{count}</div> <button onClick={plus}>+</button> <button onClick={minus}>-</button> </div> );}

Counterコンポーネントを用意

Page 33: Web Workerで○○する話

UI側

import React from 'react';class App extends React.Component {

constructor(props) { super(props); this.state = {}; this.worker = props.worker; this.worker.onmessage = evt => { this.setState(evt.data); }; } /** 右へ続く -> **/

/** 続き **/ render() { const { count } = this.state; return ( <Counter count={count} plus={this.handlePlus.bind(this)} minus={this.handleMinus.bind(this)} /> ); } /** 次のスライドへ続く **/

ControllerViewとして Appを用意

Page 34: Web Workerで○○する話

UI側

/** 前のスライドからの続き **/ handlePlus() { this.worker.postMessage({ type: 'UPDATE_COUNT', payload: { value: 1 } }); } /** 右へ続く -> **/

/** 続き **/ handleMinus() { this.worker.postMessage({ type: 'UPDATE_COUNT', payload: { value: -1 } }); }}

アプリケーションコンテナを用意

Page 35: Web Workerで○○する話

UI側

import { render } from 'react-dom';

render( <App worker={new Worker('back.js')} />, document.getElementById('app'));

アプリの起動部分

Page 36: Web Workerで○○する話

Worker側const state = { count: 0};

onmessage = evt => { postMessage(store(state, evt.data));};// initializepostMessage(state);

function store(state, action) { const { type, payload } = action; switch (type) { case 'UPDATE_COUNT': return updateCount(state, payload); default: return state; }}

function updateCount(state, payload) { const { value } = payload; state.count += value; return state;}

Page 37: Web Workerで○○する話

やってみた感じ

一方通行のデータフローの制限が自然に生まれる

メッセージングのデータは複製されるので、 UI側に渡すときに自分でステートをイミュータブルにしなくて良い

Immutable.jsとかでやらなくて良い

(雑すぎて Dispatcher 端折っちゃった)

Page 38: Web Workerで○○する話

同じようなこと考えてる人いるっぽい

参考 https://medium.com/@nsisodiya/flux-inside-web-workers-cc51fb463882#.8apt7vhfh

Page 39: Web Workerで○○する話

まとめ

Workerの中ならば必ずしも非同期じゃなくてもよい。

状況に応じて同期・非同期を選択できる。

Workerを積極的に取り入れたアーキテクチャ時代が来る(かもしれない)