let loadQueue = [] let requestPool = [] let pendingRequests = new Map() let requestCount = 0 const batchSize = 8 const debug = false function recycledXMLHttpRequest() { // https://nullprogram.com/blog/2013/02/08/ if (requestPool.length > 0) { return requestPool.pop() } requestCount += 1 if (debug) console.log('create XMLHttpRequest', requestCount) let xhr = new XMLHttpRequest() return xhr } function load() { while (loadQueue.length > 0 && pendingRequests.size < batchSize) { let tile = loadQueue.shift() let [col, row, url] = tile let xhr = recycledXMLHttpRequest() xhr.onload = event => { let buffer = xhr.response postMessage({ success: true, url, col, row, buffer }, [buffer]) pendingRequests.delete(url) } xhr.onerror = event => { let buffer = null postMessage({ success: false, url, col, row, buffer }) pendingRequests.delete(url) } xhr.onreadystatechange = () => { // In local files, status is 0 upon success in Mozilla Firefox if (xhr.readyState === XMLHttpRequest.DONE) { pendingRequests.delete(url) requestPool.push(xhr) if (debug) console.log('resuse XMLHttpRequest') } } if (debug) console.log('open XMLHttpRequest') xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' if (debug) console.log('send XMLHttpRequest') xhr.send() pendingRequests.set(url, xhr) } if (loadQueue.length > 0) setTimeout(load, 1000 / 120) else { if (debug) console.log('tileloader ready') } } self.onmessage = event => { let msg = event.data switch (msg.command) { case 'load': for (let tile of msg.tiles) { loadQueue.push(tile) } load() break case 'abort': loadQueue = [] requestPool = [] for (let xhr of pendingRequests.values()) { xhr.abort() } pendingRequests.clear() if (debug) console.log('tileloader aborted') break default: console.warn('Unknown worker command: ' + msg.command) } }