Last active
July 18, 2020 11:14
-
-
Save siddharthvp/8796b9f44a95a3d2fbaf2bb437397706 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// How to run: copy paste to console in a tab with WP open | |
Morebits.taskManager = function () { | |
this.taskDependencyMap = new Map(); | |
this.deferreds = new Map(); | |
this.allDeferreds = []; // Hack: IE doesn't support Map.prototype.values | |
/** | |
* Register a task along with its dependencies (tasks which should have finished | |
* execution before we can begin this one). Each task is a function that must return | |
* a promise. The function will get the values resolved by the dependency functions | |
* as arguments. | |
* @param {function} func - a task | |
* @param {function[]} deps - its dependencies | |
*/ | |
this.add = function (func, deps) { | |
this.taskDependencyMap.set(func, deps); | |
var deferred = $.Deferred(); | |
this.deferreds.set(func, deferred); | |
this.allDeferreds.push(deferred); | |
}; | |
/** | |
* Run all the tasks. Multiple tasks may be run at once. | |
*/ | |
this.execute = function () { | |
var self = this; // proxy for `this` for use inside functions where `this` is something else | |
this.taskDependencyMap.forEach(function (deps, task) { | |
var dependencyPromisesArray = deps.map(function (dep) { | |
return self.deferreds.get(dep); | |
}); | |
$.when.apply(null, dependencyPromisesArray).then(function () { | |
console.log('Executing ' + task.name); | |
task.apply(null, arguments).then(function () { | |
console.log(task.name + ' is resolved'); | |
self.deferreds.get(task).resolve.apply(null, arguments); | |
}); | |
}); | |
}); | |
return $.when.apply(null, this.allDeferreds).then(function () { // resolved when eveything is done! | |
console.log('done; yay!'); | |
}); | |
}; | |
/** | |
* Check if there are any cyclic dependencies. | |
* We represent the tasks as the vertices of a directed graph whose edges | |
* represent dependencies, then perform a depth-first traversal. | |
* See [[Depth-first search]] for a background on how this works. | |
* | |
* XXX: Is this worth it? | |
* Useful for testing and debugging but shouldn't be required in production code | |
*/ | |
this.checkCircularDependencies = function () { | |
var visited = new Set(); | |
var executed = new Set(); | |
var dfs = function (task) { | |
visited.add(task); | |
var circularLink; | |
var deps = this.taskDependencyMap.get(task); | |
for (var i = 0; i < deps.length; i++) { | |
if (!visited.has(deps[i])) { | |
if (circularLink = dfs(deps[i])) { | |
return circularLink; | |
} | |
} else if (!executed.has(deps[i])) { | |
return deps[i]; | |
} | |
} | |
executed.add(task); | |
}.bind(this); | |
this.taskDependencyMap.forEach(function (_, task) { | |
var link; | |
if (!visited.has(task)) { | |
if (link = dfs(task)) { | |
// link.name (which gives the name of the function) won't work in IE 11, | |
// so you're on your own in finding the circular link | |
throw new Error('circular dependency detected at ' + link.name); | |
} | |
} | |
}); | |
}; | |
}; | |
var gar = { | |
tasks: { | |
getNumber: function getNumber() { | |
var def = $.Deferred(); | |
setTimeout(function () { | |
def.resolve(4); | |
actualTimes.getNumber = new Date().getTime() - starttime; | |
}, 1500); | |
return def; | |
}, | |
createNomPage: function createNomPage() { | |
var def = $.Deferred(); | |
setTimeout(function () { | |
def.resolve(); | |
actualTimes.createNomPage = new Date().getTime() - starttime; | |
}, 2500); | |
return def; | |
}, | |
editTalkPage: function editTalkPage(num, randval) { | |
if (num !== 4) throw new Error('argument mismatch'); | |
if (randval !== 42) throw new Error('argument mismatch'); | |
var def = $.Deferred(); | |
setTimeout(function () { | |
def.resolve(); | |
actualTimes.editTalkPage = new Date().getTime() - starttime; | |
}, 1500); | |
return def; | |
}, | |
getCreator: function getCreator() { | |
var def = $.Deferred(); | |
setTimeout(function () { | |
def.resolve('Example user'); | |
actualTimes.getCreator = new Date().getTime() - starttime; | |
}, 1000); | |
return def; | |
}, | |
notifyAuthor: function notifyAuthor(creator) { | |
if (creator !== 'Example user') throw new Error('argument mismatch'); | |
var def = $.Deferred(); | |
setTimeout(function () { | |
def.resolve(42); | |
actualTimes.notifyAuthor = new Date().getTime() - starttime; | |
}, 1500); | |
return def; | |
}, | |
} | |
} | |
/*** Register the functions along with dependencies ***/ | |
var tm = new Morebits.taskManager(); | |
tm.add(gar.tasks.getNumber, []); | |
tm.add(gar.tasks.notifyAuthor, [gar.tasks.getCreator]); | |
tm.add(gar.tasks.createNomPage, [gar.tasks.getNumber]); | |
tm.add(gar.tasks.getCreator, []); | |
tm.add(gar.tasks.editTalkPage, [gar.tasks.getNumber, gar.tasks.notifyAuthor]); | |
var actualTimes = {}; | |
// Expected times are calculated as time needed for dependent functions + time needed for the | |
// function itself. | |
var expectedTimes = { | |
getNumber: 1500, | |
getCreator: 1000, | |
notifyAuthor: 1500 + 1000, | |
// 1500 for dependency getNumber, 2500 for createNomPage itself | |
createNomPage: 1500 + 2500, | |
// 1500 ms for getNumber, 1500 + 1000 for notifyAuthor | |
// 1500 ms for editTalkPage itself, | |
editTalkPage: Math.max(1500, 1500 + 1000) + 1500 | |
}; | |
// all times measured are relative to this time | |
var starttime = new Date(); | |
// Go! | |
tm.execute().then(function () { | |
$.each(expectedTimes, function (funcName, expectedTime) { | |
var actualTime = actualTimes[funcName]; | |
// allow 50 ms tolerance for actually executing the cdoe | |
if (Math.abs(actualTime - expectedTime) > 50) { | |
console.error('time taken for ' + funcName + ' (' + actualTime + ') is not as expected (' + expectedTime + ')'); | |
} | |
}); | |
console.log('All times as expected! (unless you see any red error above)'); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment