1"use strict"; 2var firstLineError; 3try {throw new Error(); } catch (e) {firstLineError = e;} 4var schedule = require("./schedule"); 5var Queue = require("./queue"); 6var util = require("./util"); 7 8function Async() { 9 this._customScheduler = false; 10 this._isTickUsed = false; 11 this._lateQueue = new Queue(16); 12 this._normalQueue = new Queue(16); 13 this._haveDrainedQueues = false; 14 this._trampolineEnabled = true; 15 var self = this; 16 this.drainQueues = function () { 17 self._drainQueues(); 18 }; 19 this._schedule = schedule; 20} 21 22Async.prototype.setScheduler = function(fn) { 23 var prev = this._schedule; 24 this._schedule = fn; 25 this._customScheduler = true; 26 return prev; 27}; 28 29Async.prototype.hasCustomScheduler = function() { 30 return this._customScheduler; 31}; 32 33Async.prototype.enableTrampoline = function() { 34 this._trampolineEnabled = true; 35}; 36 37Async.prototype.disableTrampolineIfNecessary = function() { 38 if (util.hasDevTools) { 39 this._trampolineEnabled = false; 40 } 41}; 42 43Async.prototype.haveItemsQueued = function () { 44 return this._isTickUsed || this._haveDrainedQueues; 45}; 46 47 48Async.prototype.fatalError = function(e, isNode) { 49 if (isNode) { 50 process.stderr.write("Fatal " + (e instanceof Error ? e.stack : e) + 51 "\n"); 52 process.exit(2); 53 } else { 54 this.throwLater(e); 55 } 56}; 57 58Async.prototype.throwLater = function(fn, arg) { 59 if (arguments.length === 1) { 60 arg = fn; 61 fn = function () { throw arg; }; 62 } 63 if (typeof setTimeout !== "undefined") { 64 setTimeout(function() { 65 fn(arg); 66 }, 0); 67 } else try { 68 this._schedule(function() { 69 fn(arg); 70 }); 71 } catch (e) { 72 throw new Error("No async scheduler available\u000a\u000a See http://goo.gl/MqrFmX\u000a"); 73 } 74}; 75 76function AsyncInvokeLater(fn, receiver, arg) { 77 this._lateQueue.push(fn, receiver, arg); 78 this._queueTick(); 79} 80 81function AsyncInvoke(fn, receiver, arg) { 82 this._normalQueue.push(fn, receiver, arg); 83 this._queueTick(); 84} 85 86function AsyncSettlePromises(promise) { 87 this._normalQueue._pushOne(promise); 88 this._queueTick(); 89} 90 91if (!util.hasDevTools) { 92 Async.prototype.invokeLater = AsyncInvokeLater; 93 Async.prototype.invoke = AsyncInvoke; 94 Async.prototype.settlePromises = AsyncSettlePromises; 95} else { 96 Async.prototype.invokeLater = function (fn, receiver, arg) { 97 if (this._trampolineEnabled) { 98 AsyncInvokeLater.call(this, fn, receiver, arg); 99 } else { 100 this._schedule(function() { 101 setTimeout(function() { 102 fn.call(receiver, arg); 103 }, 100); 104 }); 105 } 106 }; 107 108 Async.prototype.invoke = function (fn, receiver, arg) { 109 if (this._trampolineEnabled) { 110 AsyncInvoke.call(this, fn, receiver, arg); 111 } else { 112 this._schedule(function() { 113 fn.call(receiver, arg); 114 }); 115 } 116 }; 117 118 Async.prototype.settlePromises = function(promise) { 119 if (this._trampolineEnabled) { 120 AsyncSettlePromises.call(this, promise); 121 } else { 122 this._schedule(function() { 123 promise._settlePromises(); 124 }); 125 } 126 }; 127} 128 129function _drainQueue(queue) { 130 while (queue.length() > 0) { 131 _drainQueueStep(queue); 132 } 133} 134 135function _drainQueueStep(queue) { 136 var fn = queue.shift(); 137 if (typeof fn !== "function") { 138 fn._settlePromises(); 139 } else { 140 var receiver = queue.shift(); 141 var arg = queue.shift(); 142 fn.call(receiver, arg); 143 } 144} 145 146Async.prototype._drainQueues = function () { 147 _drainQueue(this._normalQueue); 148 this._reset(); 149 this._haveDrainedQueues = true; 150 _drainQueue(this._lateQueue); 151}; 152 153Async.prototype._queueTick = function () { 154 if (!this._isTickUsed) { 155 this._isTickUsed = true; 156 this._schedule(this.drainQueues); 157 } 158}; 159 160Async.prototype._reset = function () { 161 this._isTickUsed = false; 162}; 163 164module.exports = Async; 165module.exports.firstLineError = firstLineError; 166