1"use strict"; 2 3var domain; // The domain module is executed on demand 4var hasSetImmediate = typeof setImmediate === "function"; 5 6// Use the fastest means possible to execute a task in its own turn, with 7// priority over other events including network IO events in Node.js. 8// 9// An exception thrown by a task will permanently interrupt the processing of 10// subsequent tasks. The higher level `asap` function ensures that if an 11// exception is thrown by a task, that the task queue will continue flushing as 12// soon as possible, but if you use `rawAsap` directly, you are responsible to 13// either ensure that no exceptions are thrown from your task, or to manually 14// call `rawAsap.requestFlush` if an exception is thrown. 15module.exports = rawAsap; 16function rawAsap(task) { 17 if (!queue.length) { 18 requestFlush(); 19 flushing = true; 20 } 21 // Avoids a function call 22 queue[queue.length] = task; 23} 24 25var queue = []; 26// Once a flush has been requested, no further calls to `requestFlush` are 27// necessary until the next `flush` completes. 28var flushing = false; 29// The position of the next task to execute in the task queue. This is 30// preserved between calls to `flush` so that it can be resumed if 31// a task throws an exception. 32var index = 0; 33// If a task schedules additional tasks recursively, the task queue can grow 34// unbounded. To prevent memory excaustion, the task queue will periodically 35// truncate already-completed tasks. 36var capacity = 1024; 37 38// The flush function processes all tasks that have been scheduled with 39// `rawAsap` unless and until one of those tasks throws an exception. 40// If a task throws an exception, `flush` ensures that its state will remain 41// consistent and will resume where it left off when called again. 42// However, `flush` does not make any arrangements to be called again if an 43// exception is thrown. 44function flush() { 45 while (index < queue.length) { 46 var currentIndex = index; 47 // Advance the index before calling the task. This ensures that we will 48 // begin flushing on the next task the task throws an error. 49 index = index + 1; 50 queue[currentIndex].call(); 51 // Prevent leaking memory for long chains of recursive calls to `asap`. 52 // If we call `asap` within tasks scheduled by `asap`, the queue will 53 // grow, but to avoid an O(n) walk for every task we execute, we don't 54 // shift tasks off the queue after they have been executed. 55 // Instead, we periodically shift 1024 tasks off the queue. 56 if (index > capacity) { 57 // Manually shift all values starting at the index back to the 58 // beginning of the queue. 59 for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) { 60 queue[scan] = queue[scan + index]; 61 } 62 queue.length -= index; 63 index = 0; 64 } 65 } 66 queue.length = 0; 67 index = 0; 68 flushing = false; 69} 70 71rawAsap.requestFlush = requestFlush; 72function requestFlush() { 73 // Ensure flushing is not bound to any domain. 74 // It is not sufficient to exit the domain, because domains exist on a stack. 75 // To execute code outside of any domain, the following dance is necessary. 76 var parentDomain = process.domain; 77 if (parentDomain) { 78 if (!domain) { 79 // Lazy execute the domain module. 80 // Only employed if the user elects to use domains. 81 domain = require("domain"); 82 } 83 domain.active = process.domain = null; 84 } 85 86 // `setImmediate` is slower that `process.nextTick`, but `process.nextTick` 87 // cannot handle recursion. 88 // `requestFlush` will only be called recursively from `asap.js`, to resume 89 // flushing after an error is thrown into a domain. 90 // Conveniently, `setImmediate` was introduced in the same version 91 // `process.nextTick` started throwing recursion errors. 92 if (flushing && hasSetImmediate) { 93 setImmediate(flush); 94 } else { 95 process.nextTick(flush); 96 } 97 98 if (parentDomain) { 99 domain.active = process.domain = parentDomain; 100 } 101} 102