yoastseo-dep
Version:
Yoast clientside page analysis
165 lines (145 loc) • 3.92 kB
JavaScript
// External dependencies.
import { merge } from "lodash-es";
// Internal dependencies.
import Task from "./Task";
const DEFAULT_CONFIGURATION = {
pollTime: 50,
};
/**
* The scheduler is used in the analysis web worker to schedule tasks.
*
* Tasks have priorities based on their type.
* When a task is executed, the task id and data are its arguments.
* When a task is done, the task id and the execute result are its arguments.
*
* Start polling runs tick.
* 1. Tick tries to run the next task.
* 2. After the task is run, a timeout is set (configuration.pollTime).
* 3. On the timeout execution, tick is called again (back to step 1).
*/
export default class Scheduler {
/**
* Initializes a Scheduler.
*
* @param {Object} [configuration] The configuration.
* @param {number} [configuration.pollTime] The time in between each task
* poll in milliseconds,
* defaults to 50.
*/
constructor( configuration = {} ) {
this._configuration = merge( DEFAULT_CONFIGURATION, configuration );
this._tasks = {
standard: [],
extensions: [],
analyze: [],
analyzeRelatedKeywords: [],
};
this._pollHandle = null;
this._started = false;
// Bind functions to this scope.
this.startPolling = this.startPolling.bind( this );
this.stopPolling = this.stopPolling.bind( this );
this.tick = this.tick.bind( this );
}
/**
* Initialize polling.
*
* @returns {void}
*/
startPolling() {
if ( this._started ) {
return;
}
this._started = true;
this.tick();
}
/**
* Do a tick and execute a task.
*
* @returns {void}
*/
tick() {
this.executeNextTask()
.then( () => {
this._pollHandle = setTimeout( this.tick, this._configuration.pollTime );
} );
}
/**
* Stop polling.
*
* @returns {void}
*/
stopPolling() {
clearTimeout( this._pollHandle );
this._pollHandle = null;
this._started = false;
}
/**
* Schedule a task.
*
* @param {Object} task The task object.
* @param {number} task.id The task id.
* @param {function} task.execute The function to run for task execution.
* @param {function} task.done The function to run when the task is done.
* @param {Object} task.data The data object to execute with.
* @param {string} task.type The type of the task.
*
* @returns {void}
*/
schedule( { id, execute, done, data, type } ) {
const task = new Task( id, execute, done, data, type );
switch ( type ) {
case "customMessage":
case "loadScript":
this._tasks.extensions.push( task );
break;
case "analyze":
this._tasks.analyze = [ task ];
break;
case "analyzeRelatedKeywords":
this._tasks.analyzeRelatedKeywords = [ task ];
break;
default:
this._tasks.standard.push( task );
}
}
/**
* Retrieves the next task from the queue. Queues are sorted from lowest to highest priority.
*
* @returns {Task|null} The next task or null if none are available.
*/
getNextTask() {
if ( this._tasks.extensions.length > 0 ) {
return this._tasks.extensions.shift();
}
if ( this._tasks.analyze.length > 0 ) {
return this._tasks.analyze.shift();
}
if ( this._tasks.analyzeRelatedKeywords.length > 0 ) {
return this._tasks.analyzeRelatedKeywords.shift();
}
if ( this._tasks.standard.length > 0 ) {
return this._tasks.standard.shift();
}
return null;
}
/**
* Executes the next task.
*
* @returns {Promise} Resolves once the task is done, with the result of the task.
*/
executeNextTask() {
const task = this.getNextTask();
if ( task === null ) {
return Promise.resolve( null );
}
return Promise.resolve()
.then( () => {
return task.execute( task.id, task.data );
} )
.then( ( result ) => {
task.done( task.id, result );
return result;
} );
}
}