////////////////////////////////////////////////////////////////////////////////// // Task API (Time-sharing based `async`, `setTimeout`, `setInterval`, `Promise` implementation in WSH.js) ///////////////////////////////////////////////////////////////////////////////// /* // example: * // var TASK = require("lib/task"); * // var taskQueue = TASK.createTaskQueue(); * // TASK.putTask(queue, TASK.createTask(function(task, a, b, c) { console.log(a + b + c); sleep(100); }, [1, 2, 3])) * // .then(TASK.createTask(function(task, a, b, c) { console.log(a + b + c); sleep(200); }, [4, 5, 6])) * // .then(TASK.createTask(function(task, a, b, c) { console.log(a + b + c); sleep(300); }, [7, 8, 9])) * // ; * // TASK.putTask(queue, TASK.createTask(function(task, a, b, c) { console.log(a + b + c); sleep(100); }, [3, 2, 1]) * // .then(TASK.createTask(function(task, a, b, c) { console.log(a + b + c); sleep(200); }, [6, 5, 4])) * // .then(TASK.createTask(function(task, a, b, c) { TASK.stop(); console.log(a + b + c); sleep(300); }, [9, 8, 7])) * // ; * // taskQueue.run(); */ var Task = function(f, params) { this.f = f; this.params = params; this.nextTask = null; this.failTask = null; this.when = 0; this.delay = 0; this.callCount = 0; this.setNextTask = function(task) { this.nextTask = task; return this; }; this.setFailTask = function(task) { this.failTask = task; return this; }; this.setWhen = function(when) { this.when = when; return this; }; this.setDelay = function(delay) { this.delay = delay; return this; }; this.upCallCount = function() { this.callCount++; return this; }; this.resetCount = function() { this.callCount = 0; return this; }; }; var TaskQueue = function() { this._task = null; this._keepalive = true; this.queue = []; this.put = function(task, delay) { var now = new Date(); try { task.setWhen(now.getTime()); if (typeof(delay) !== "undefined") { task.setDelay(delay); } this._task = task; this.queue.push(task); } catch(e) { console.error("TaskQueue.put exception: " + e.message); } return this; }; this.get = function() { var task = null; var now = new Date(); try { if (this.queue.length > 0) { var delta = this.queue.map(function(task) { return task.when + task.delay - now.getTime(); }); var i = delta.indexOf(Math.min.apply(Math, delta)); var d = this.queue[i].when + this.queue[i].delay - now.getTime(); if (i > -1 && d <= 0) { console.log("Task", i, d); task = this.queue.splice(i, 1)[0]; } } } catch(e) { console.error("TaskQueue.get: " + e.message); } return task; }; this.next = function() { var result = null; try { var task = this.get(); if (task != null) { try { result = task.f.apply(Task, [task].concat(task.params)); if (task.nextTask != null) { this.put(task.nextTask, task.nextTask.delay); } task.upCallCount(); // 호출 횟수 기록 } catch (e) { console.error("Task exception: " + e.message); console.error("task.f: " + typeof(task.f)); console.error("task.params: " + typeof(task.params)); // 태스크 개별 오류 처리 if (task.failTask) { this.put(task.failTask, 0); } } } } catch(e) { console.error("TaskQueue.next: " + e.message); } return result; }; this.then = function(task, delay) { try { if (typeof(delay) !== "undefined") { task.setDelay(delay); } this._task.setNextTask(task); this._task = task; return this; } catch(e) { console.error("TaskQueue.then: " + e.message); console.error(this._task); } }; this.run = function() { this._keepalive = true; while(this._keepalive) { this.next(); sleep(1); } }; this.stop = function() { this._keepalive = false; }; }; exports.createTaskQueue = function() { return new TaskQueue(); }; exports.createTask = function(f, params, failTask) { try { return (new Task(f, params)).setFailTask(failTask); } catch(e) { console.error("createTask exception: " + e.message); } }; exports.putTask = function(q, task, delay) { try { if (q instanceof TaskQueue && task instanceof Task) { return q.put(task, delay); } } catch(e) { console.error("putTask exception: " + e.message); } }; exports.nextTask = function(q) { try { return q.next(); } catch(e) { console.error("nextTask exception: " + e.message); } }; exports.run = function(q) { q.run(); }; exports.stop = function(q) { q.stop(); }; //////////////////////////////////////////////////// // START Global functions //////////////////////////////////////////////////// var __taskQueue__ = new TaskQueue(); function setTimeout(f, delay) { var params = (arguments.length > 2 ? arguments.slice(2) : []); var task = new Task(f, delay); __taskQueue__.put(task); } // TODO: compatiblity with clearInterval() function setInterval(f, delay) { var params = (arguments.length > 2 ? arguments.slice(2) : []); var task = new Task(f, delay); task.setNextTask(task); __taskQueue__.put(task); } function Promise(executor) { this.length = 1; this.executor = executor; this.all = function(iterable) { // todo }; this.race = function(iterable) { // todo }; this.reject = function(reason) { // todo }; this.resolve = function(reason) { // todo }; this.then = function(successCallback, failureCallback) { // todo }; this.catch = function(failureCallback) { // todo }; this.executor(this.resolve, this.reject); } exports.__taskQueue__ = __taskQueue__; exports.setTimeout = setTimeout; exports.setInterval = setInterval; exports.Promise = Promise; //////////////////////////////////////////////////// // END Global functions //////////////////////////////////////////////////// exports.Task = Task; exports.TaskQueue = TaskQueue; exports.VERSIONINFO = "Task Module (task.js) version 0.2"; exports.global = global; exports.require = require;