1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5'use strict'; 6 7/** 8 * Worker for requests. Fetches requests from a queue and processes them 9 * synchronously, taking into account priorities. The highest priority is 0. 10 */ 11function Worker() { 12 /** 13 * List of requests waiting to be checked. If these items are available in 14 * cache, then they are processed immediately after starting the worker. 15 * However, if they have to be downloaded, then these requests are moved 16 * to pendingRequests_. 17 * 18 * @type {Array.<Request>} 19 * @private 20 */ 21 this.newRequests_ = []; 22 23 /** 24 * List of pending requests for images to be downloaded. 25 * @type {Array.<Request>} 26 * @private 27 */ 28 this.pendingRequests_ = []; 29 30 /** 31 * List of requests being processed. 32 * @type {Array.<Request>} 33 * @private 34 */ 35 this.activeRequests_ = []; 36 37 /** 38 * Hash array of requests being added to the queue, but not finalized yet. 39 * @type {Object} 40 * @private 41 */ 42 this.requests_ = {}; 43 44 /** 45 * If the worker has been started. 46 * @type {boolean} 47 * @private 48 */ 49 this.started_ = false; 50} 51 52/** 53 * Maximum download requests to be run in parallel. 54 * @type {number} 55 * @const 56 */ 57Worker.MAXIMUM_IN_PARALLEL = 5; 58 59/** 60 * Adds a request to the internal priority queue and executes it when requests 61 * with higher priorities are finished. If the result is cached, then it is 62 * processed immediately once the worker is started. 63 * 64 * @param {Request} request Request object. 65 */ 66Worker.prototype.add = function(request) { 67 if (!this.started_) { 68 this.newRequests_.push(request); 69 this.requests_[request.getId()] = request; 70 return; 71 } 72 73 // Enqueue the request, since already started. 74 this.pendingRequests_.push(request); 75 this.sortPendingRequests_(); 76 77 this.continue_(); 78}; 79 80/** 81 * Removes a request from the worker (if exists). 82 * @param {string} requestId Unique ID of the request. 83 */ 84Worker.prototype.remove = function(requestId) { 85 var request = this.requests_[requestId]; 86 if (!request) 87 return; 88 89 // Remove from the internal queues with pending tasks. 90 var newIndex = this.pendingRequests_.indexOf(request); 91 if (newIndex != -1) 92 this.newRequests_.splice(newIndex, 1); 93 var pendingIndex = this.pendingRequests_.indexOf(request); 94 if (pendingIndex != -1) 95 this.pendingRequests_.splice(pendingIndex, 1); 96 97 // Cancel the request. 98 request.cancel(); 99 delete this.requests_[requestId]; 100}; 101 102/** 103 * Starts handling requests. 104 */ 105Worker.prototype.start = function() { 106 this.started_ = true; 107 108 // Process tasks added before worker has been started. 109 this.pendingRequests_ = this.newRequests_; 110 this.sortPendingRequests_(); 111 this.newRequests_ = []; 112 113 // Start serving enqueued requests. 114 this.continue_(); 115}; 116 117/** 118 * Sorts pending requests by priorities. 119 * @private 120 */ 121Worker.prototype.sortPendingRequests_ = function() { 122 this.pendingRequests_.sort(function(a, b) { 123 return a.getPriority() - b.getPriority(); 124 }); 125}; 126 127/** 128 * Processes pending requests from the queue. There is no guarantee that 129 * all of the tasks will be processed at once. 130 * 131 * @private 132 */ 133Worker.prototype.continue_ = function() { 134 // Run only up to MAXIMUM_IN_PARALLEL in the same time. 135 while (this.pendingRequests_.length && 136 this.activeRequests_.length < Worker.MAXIMUM_IN_PARALLEL) { 137 var request = this.pendingRequests_.shift(); 138 this.activeRequests_.push(request); 139 140 // Try to load from cache. If doesn't exist, then download. 141 request.loadFromCacheAndProcess( 142 this.finish_.bind(this, request), 143 function(currentRequest) { 144 currentRequest.downloadAndProcess( 145 this.finish_.bind(this, currentRequest)); 146 }.bind(this, request)); 147 } 148}; 149 150/** 151 * Handles finished requests. 152 * 153 * @param {Request} request Finished request. 154 * @private 155 */ 156Worker.prototype.finish_ = function(request) { 157 var index = this.activeRequests_.indexOf(request); 158 if (index < 0) 159 console.warn('Request not found.'); 160 this.activeRequests_.splice(index, 1); 161 delete this.requests_[request.getId()]; 162 163 // Continue handling the most important requests (if started). 164 if (this.started_) 165 this.continue_(); 166}; 167