diff --git a/lib/ImageWorker.js b/lib/ImageWorker.js index 743e1c9..53869de 100644 --- a/lib/ImageWorker.js +++ b/lib/ImageWorker.js @@ -3,15 +3,10 @@ import React, { Component } from 'react'; const webWorkerScript = ` + const handleResponse = response => response.blob(); self.addEventListener('message', event => { const url = event.data; - fetch(url, { - method: 'GET', - mode: 'no-cors', - cache: 'default' - }).then(response => { - return response.blob(); - }).then(_ => postMessage(url)); + fetch(url, { mode: 'no-cors' }).then(handleResponse).then(() => postMessage(url)); }) `; @@ -34,11 +29,44 @@ const wrappedComponent = WrappedComponent => props => { return ; }; +/** Have we initiated the worker pool already? */ +let workerPoolCreated = false; +const workerPool = []; + +function createWorkerPool() { + const blobURL = URL.createObjectURL( + new Blob([webWorkerScript], { type: 'application/javascript' }) + ); + for (let i = 0; i < window.navigator.hardwareConcurrency || 4; ++i) { + workerPool.push({ + worker: new Worker(blobURL), + inUse: false, + i + }); + } +} + +/** Returns the next available worker. */ +function getNextWorker() { + for (let i = 0; i < workerPool.length; ++i) { + const worker = workerPool[i]; + if (!worker.inUse) { + worker.inUse = true; + return worker; + } + } + // no free found, so we just return the first one + return workerPool[0]; +} + +/** Marks worker `index` as available. */ +function setFree(index: number) { + workerPool[index].inUse = false; +} + + class ImageWorker extends Component { image: HTMLImageElement; - worker = new Worker(URL.createObjectURL( - new Blob([webWorkerScript], { type: 'application/javascript' }) - )) state = { isLoading: true, @@ -46,21 +74,26 @@ class ImageWorker extends Component { } constructor(props: ImageWorkerProps) { super(props); - this.worker.onmessage = (event: Object) => { + this.image = null; + + if (!workerPoolCreated) { + workerPoolCreated = true; + createWorkerPool(); + } + + const workerObj = getNextWorker(); + workerObj.worker.onmessage = (event: Object) => { this.loadImage(event.data); + setFree(workerObj.i); }; - } - - componentDidMount() { - this.worker.postMessage(this.props.src); + workerObj.worker.postMessage(props.src); } componentWillUnmount() { - if (this.image) { + if (this.image !== null) { this.image.onload = null; this.image.onerror = null; } - this.worker.terminate(); } renderPlaceholder() { @@ -70,9 +103,8 @@ class ImageWorker extends Component { return ; } else if (typeof placeholder === 'string') { return placeholder; - } else { - return null; } + return null; } loadImage = (url: string) => {