Comparing version 1.0.0 to 2.0.0
158
asap.js
@@ -0,113 +1,65 @@ | ||
"use strict"; | ||
// Use the fastest possible means to execute a task in a future turn | ||
// of the event loop. | ||
var rawAsap = require("./raw"); | ||
var freeTasks = []; | ||
// linked list of tasks (single, with head node) | ||
var head = {task: void 0, next: null}; | ||
var tail = head; | ||
var flushing = false; | ||
var requestFlush = void 0; | ||
var isNodeJS = false; | ||
function flush() { | ||
/* jshint loopfunc: true */ | ||
while (head.next) { | ||
head = head.next; | ||
var task = head.task; | ||
head.task = void 0; | ||
var domain = head.domain; | ||
if (domain) { | ||
head.domain = void 0; | ||
domain.enter(); | ||
} | ||
try { | ||
task(); | ||
} catch (e) { | ||
if (isNodeJS) { | ||
// In node, uncaught exceptions are considered fatal errors. | ||
// Re-throw them synchronously to interrupt flushing! | ||
// Ensure continuation if the uncaught exception is suppressed | ||
// listening "uncaughtException" events (as domains does). | ||
// Continue in next event to avoid tick recursion. | ||
if (domain) { | ||
domain.exit(); | ||
} | ||
setTimeout(flush, 0); | ||
if (domain) { | ||
domain.enter(); | ||
} | ||
throw e; | ||
} else { | ||
// In browsers, uncaught exceptions are not fatal. | ||
// Re-throw them asynchronously to avoid slow-downs. | ||
setTimeout(function() { | ||
throw e; | ||
}, 0); | ||
} | ||
} | ||
if (domain) { | ||
domain.exit(); | ||
} | ||
/** | ||
* Calls a task as soon as possible after returning, in its own event, with | ||
* priority over IO events. An exception thrown in a task can be handled by | ||
* `process.on("uncaughtException") or `domain.on("error")`, but will otherwise | ||
* crash the process. If the error is handled, all subsequent tasks will | ||
* resume. | ||
* | ||
* @param {{call}} task A callable object, typically a function that takes no | ||
* arguments. | ||
*/ | ||
module.exports = asap; | ||
function asap(task) { | ||
var rawTask; | ||
if (freeTasks.length) { | ||
rawTask = freeTasks.pop(); | ||
} else { | ||
rawTask = new RawTask(); | ||
} | ||
flushing = false; | ||
rawTask.task = task; | ||
rawTask.domain = process.domain; | ||
rawAsap(rawTask); | ||
} | ||
if (typeof process !== "undefined" && process.nextTick) { | ||
// Node.js before 0.9. Note that some fake-Node environments, like the | ||
// Mocha test runner, introduce a `process` global without a `nextTick`. | ||
isNodeJS = true; | ||
requestFlush = function () { | ||
process.nextTick(flush); | ||
}; | ||
} else if (typeof setImmediate === "function") { | ||
// In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate | ||
if (typeof window !== "undefined") { | ||
requestFlush = setImmediate.bind(window, flush); | ||
} else { | ||
requestFlush = function () { | ||
setImmediate(flush); | ||
}; | ||
} | ||
} else if (typeof MessageChannel !== "undefined") { | ||
// modern browsers | ||
// http://www.nonblocking.io/2011/06/windownexttick.html | ||
var channel = new MessageChannel(); | ||
channel.port1.onmessage = flush; | ||
requestFlush = function () { | ||
channel.port2.postMessage(0); | ||
}; | ||
} else { | ||
// old browsers | ||
requestFlush = function () { | ||
setTimeout(flush, 0); | ||
}; | ||
function RawTask() { | ||
this.task = null; | ||
this.domain = null; | ||
} | ||
function asap(task) { | ||
tail = tail.next = { | ||
task: task, | ||
domain: isNodeJS && process.domain, | ||
next: null | ||
}; | ||
if (!flushing) { | ||
flushing = true; | ||
requestFlush(); | ||
RawTask.prototype.call = function () { | ||
if (this.domain) { | ||
this.domain.enter(); | ||
} | ||
var threw = true; | ||
try { | ||
this.task.call(); | ||
threw = false; | ||
// If the task throws an exception (presumably) Node.js restores the | ||
// domain stack for the next event. | ||
if (this.domain) { | ||
this.domain.exit(); | ||
} | ||
} finally { | ||
// We use try/finally and a threw flag to avoid messing up stack traces | ||
// when we catch and release errors. | ||
if (threw) { | ||
// In Node.js, uncaught exceptions are considered fatal errors. | ||
// Re-throw them to interrupt flushing! | ||
// Ensure that flushing continues if an uncaught exception is | ||
// suppressed listening process.on("uncaughtException") or | ||
// domain.on("error"). | ||
rawAsap.requestFlush(); | ||
} | ||
// If the task threw an error, we do not want to exit the domain here. | ||
// Exiting the domain would prevent the domain from catching the error. | ||
this.task = null; | ||
this.domain = null; | ||
freeTasks.push(this); | ||
} | ||
}; | ||
module.exports = asap; | ||
{ | ||
"name": "asap", | ||
"version": "1.0.0", | ||
"description": "High-priority task queue for Node.js and browsers", | ||
"keywords": ["event", "task", "queue"], | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "https://github.com/kriskowal/asap/raw/master/LICENSE.md" | ||
} | ||
], | ||
"main": "asap" | ||
"name": "asap", | ||
"version": "2.0.0", | ||
"description": "High-priority task queue for Node.js and browsers", | ||
"keywords": [ | ||
"event", | ||
"task", | ||
"queue" | ||
], | ||
"license": { | ||
"type": "MIT", | ||
"url": "https://github.com/kriskowal/asap/raw/master/LICENSE.md" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/kriskowal/asap.git" | ||
}, | ||
"main": "./asap.js", | ||
"browser": { | ||
"asap": "./browser-asap.js", | ||
"raw": "./browser-raw.js", | ||
"test/domain": "./test/browser-domain.js" | ||
}, | ||
"files": [ | ||
"raw.js", | ||
"asap.js", | ||
"browser-raw.js", | ||
"browser-asap.js" | ||
], | ||
"scripts": { | ||
"test": "npm run test-node && npm run lint", | ||
"test-node": "node test/asap-test.js", | ||
"test-publish": "node scripts/publish-bundle.js test/asap-test.js | pbcopy", | ||
"test-browser": "node scripts/publish-bundle.js test/asap-test.js | xargs opener", | ||
"test-saucelabs": "node scripts/saucelabs.js test/asap-test.js scripts/saucelabs-spot-configurations.json", | ||
"test-saucelabs-all": "node scripts/saucelabs.js test/asap-test.js scripts/saucelabs-all-configurations.json", | ||
"test-saucelabs-worker": "node scripts/saucelabs-worker-test.js scripts/saucelabs-spot-configurations.json", | ||
"test-saucelabs-worker-all": "node scripts/saucelabs-worker-test.js scripts/saucelabs-all-configurations.json", | ||
"lint": "jshint raw.js asap.js browser-raw.js browser-asap.js scripts" | ||
}, | ||
"devDependencies": { | ||
"events": "^1.0.1", | ||
"opener": "^1.3.0", | ||
"jshint": "^2.5.1", | ||
"q": "^2.0.1", | ||
"knox": "^0.8.10", | ||
"saucelabs": "^0.1.1", | ||
"wd": "^0.2.21", | ||
"mr": "^2.0.3", | ||
"q-io": "^2.0.3", | ||
"weak-map": "^1.0.5" | ||
} | ||
} |
213
README.md
@@ -1,8 +0,13 @@ | ||
# ASAP | ||
This `asap` CommonJS package contains a single `asap` module that | ||
exports a single `asap` function that executes a function **as soon as | ||
possible**. | ||
[![Build Status](https://travis-ci.org/kriskowal/asap.png?branch=master)](https://travis-ci.org/kriskowal/asap) | ||
Promise and asynchronous observer libraries, as well as hand-rolled callback | ||
programs and libraries, often need a mechanism to postpone the execution of a | ||
callback until the next available event. | ||
(See [Designing API’s for Asynchrony][Zalgo].) | ||
The `asap` function executes a task **as soon as possible** but not before it | ||
returns, waiting only for the completion of the current event and previously | ||
scheduled tasks. | ||
```javascript | ||
@@ -14,24 +19,51 @@ asap(function () { | ||
More formally, ASAP provides a fast event queue that will execute tasks | ||
until it is empty before yielding to the JavaScript engine's underlying | ||
event-loop. When the event queue becomes non-empty, ASAP schedules a | ||
flush event, preferring for that event to occur before the JavaScript | ||
engine has an opportunity to perform IO tasks or rendering, thus making | ||
the first task and subsequent tasks semantically indistinguishable. | ||
ASAP uses a variety of techniques to preserve this invariant on | ||
different versions of browsers and NodeJS. | ||
[Zalgo]: http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony | ||
By design, ASAP can starve the event loop on the theory that, if there | ||
is enough work to be done synchronously, albeit in separate events, long | ||
enough to starve input or output, it is a strong indicator that the | ||
program needs to push back on scheduling more work. | ||
This CommonJS package provides an `asap` module that exports a function that | ||
executes a task function *as soon as possible*. | ||
Take care. ASAP can sustain infinite recursive calls indefinitely | ||
without warning. This is behaviorally equivalent to an infinite loop. | ||
It will not halt from a stack overflow, but it *will* chew through | ||
memory (which is an oddity I cannot explain at this time). Just as with | ||
infinite loops, you can monitor a Node process for this behavior with a | ||
heart-beat signal. As with infinite loops, a very small amount of | ||
caution goes a long way to avoiding problems. | ||
ASAP strives to schedule events to occur before yielding for IO, reflow, | ||
or redrawing. | ||
Each event receives an independent stack, with only platform code in parent | ||
frames and the events run in the order they are scheduled. | ||
ASAP provides a fast event queue that will execute tasks until it is | ||
empty before yielding to the JavaScript engine's underlying event-loop. | ||
When a task gets added to a previously empty event queue, ASAP schedules a flush | ||
event, preferring for that event to occur before the JavaScript engine has an | ||
opportunity to perform IO tasks or rendering, thus making the first task and | ||
subsequent tasks semantically indistinguishable. | ||
ASAP uses a variety of techniques to preserve this invariant on different | ||
versions of browsers and Node.js. | ||
By design, ASAP prevents input events from being handled until the task | ||
queue is empty. | ||
If the process is busy enough, this may cause incoming connection requests to be | ||
dropped, and may cause existing connections to inform the sender to reduce the | ||
transmission rate or stall. | ||
ASAP allows this on the theory that, if there is enough work to do, there is no | ||
sense in looking for trouble. | ||
As a consequence, ASAP can interfere with smooth animation. | ||
If your task should be tied to the rendering loop, consider using | ||
`requestAnimationFrame` instead. | ||
A long sequence of tasks can also effect the long running script dialog. | ||
If this is a problem, you may be able to use ASAP’s cousin `setImmediate` to | ||
break long processes into shorter intervals and periodically allow the browser | ||
to breathe. | ||
`setImmediate` will yield for IO, reflow, and repaint events. | ||
It also returns a handler and can be canceled. | ||
For a `setImmediate` shim, consider [YuzuJS setImmediate][setImmediate]. | ||
[setImmediate]: https://github.com/YuzuJS/setImmediate | ||
Take care. | ||
ASAP can sustain infinite recursive calls without warning. | ||
It will not halt from a stack overflow, and it will not consume unbounded | ||
memory. | ||
This is behaviorally equivalent to an infinite loop. | ||
Just as with infinite loops, you can monitor a Node.js process for this behavior | ||
with a heart-beat signal. | ||
As with infinite loops, a very small amount of caution goes a long way to | ||
avoiding problems. | ||
```javascript | ||
@@ -44,13 +76,91 @@ function loop() { | ||
ASAP is distinct from `setImmediate` in that it does not suffer the | ||
overhead of returning a handle and being possible to cancel. For a | ||
`setImmediate` shim, consider [setImmediate][]. | ||
In browsers, if a task throws an exception, it will not interrupt the flushing | ||
of high-priority tasks. | ||
The exception will be postponed to a later, low-priority event to avoid | ||
slow-downs. | ||
In Node.js, if a task throws an exception, ASAP will resume flushing only if—and | ||
only after—the error is handled by `domain.on("error")` or | ||
`process.on("uncaughtException")`. | ||
[setImmediate]: https://github.com/noblejs/setimmediate | ||
## Raw ASAP | ||
If a task throws an exception, it will not interrupt the flushing of | ||
high-priority tasks. The exception will be postponed to a later, | ||
low-priority event to avoid slow-downs, when the underlying JavaScript | ||
engine will treat it as it does any unhandled exception. | ||
Checking for exceptions comes at a cost. | ||
The package also provides an `asap/raw` module that exports the underlying | ||
implementation which is faster but stalls if a task throws an exception. | ||
This internal version of the ASAP function does not check for errors. | ||
If a task does throw an error, it will stall the event queue unless you manually | ||
call `rawAsap.requestFlush()` before throwing the error, or any time after. | ||
In Node.js, `asap/raw` also runs all tasks outside any domain. | ||
If you need a task to be bound to your domain, you will have to do it manually. | ||
```js | ||
if (process.domain) { | ||
task = process.domain.bind(task); | ||
} | ||
rawAsap(task); | ||
``` | ||
## Tasks | ||
A task may be any object that implements `call()`. | ||
A function will suffice, but closures tend not to be reusable and can cause | ||
garbage collector churn. | ||
Both `asap` and `rawAsap` accept task objects to give you the option of | ||
recycling task objects or using higher callable object abstractions. | ||
See the `asap` source for an illustration. | ||
## Caveats | ||
When a task is added to an empty event queue, it is not always possible to | ||
guarantee that the task queue will begin flushing immediately after the current | ||
event. | ||
However, once the task queue begins flushing, it will not yield until the queue | ||
is empty, even if the queue grows while executing tasks. | ||
The following browsers allow the use of [DOM mutation observers][] to access | ||
the HTML [microtask queue][], and thus begin flushing ASAP's task queue | ||
immediately at the end of the current event loop turn, before any rendering or | ||
IO: | ||
[microtask queue]: http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#microtask-queue | ||
[DOM mutation observers]: http://dom.spec.whatwg.org/#mutation-observers | ||
- Android 4–4.3 | ||
- Chrome 26–34 | ||
- Firefox 14–29 | ||
- Internet Explorer 11 | ||
- iPad Safari 6–7.1 | ||
- iPhone Safari 7–7.1 | ||
- Safari 6–7 | ||
In the absense of mutation observers, there are a few browsers, and situations | ||
like web workers in some of the above browsers, where [message channels][] | ||
would be a useful way to avoid falling back to timers. | ||
Message channels give direct access to the HTML [task queue][], so the ASAP | ||
task queue would flush after any already queued rendering and IO tasks, but | ||
without having the minimum delay imposed by timers. | ||
However, among these browsers, Internet Explorer 10 and Safari do not reliably | ||
dispatch messages, so they are not worth the trouble to implement. | ||
[message channels]: http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#message-channels | ||
[task queue]: http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#concept-task | ||
- Internet Explorer 10 | ||
- Safair 5.0-1 | ||
- Opera 11-12 | ||
In the absense of mutation observers, these browsers and the following browsers | ||
all fall back to using `setTimeout` and `setInterval` to ensure that a `flush` | ||
occurs. | ||
The implementation uses both and cancels whatever handler loses the race, since | ||
`setTimeout` tends to occasionally skip tasks in unisolated circumstances. | ||
Timers generally delay the flushing of ASAP's task queue for four milliseconds. | ||
- Firefox 3–13 | ||
- Internet Explorer 6–10 | ||
- iPad Safari 4.3 | ||
- Lynx 2.8.7 | ||
## Heritage | ||
@@ -63,9 +173,9 @@ | ||
Since then, Internet Explorer proposed and implemented `setImmediate`. | ||
Robert Kratić began contributing to Q by measuring the performance of | ||
Robert Katić began contributing to Q by measuring the performance of | ||
the internal implementation of `asap`, paying particular attention to | ||
error recovery. Domenic, Robert, and I collectively settled on the | ||
current strategy of unrolling the high-priority event queue internally | ||
regardless of what strategy we used to dispatch the potentially | ||
lower-priority flush event. Domenic went on to make ASAP cooperate with | ||
NodeJS domains. | ||
error recovery. | ||
Domenic, Robert, and Kris Kowal collectively settled on the current strategy of | ||
unrolling the high-priority event queue internally regardless of what strategy | ||
we used to dispatch the potentially lower-priority flush event. | ||
Domenic went on to make ASAP cooperate with Node.js domains. | ||
@@ -80,6 +190,33 @@ [Q]: https://github.com/kriskowal/q | ||
Ember’s RSVP promise implementation later [adopted][RSVP ASAP] the name ASAP but | ||
further developed the implentation. | ||
Particularly, The `MessagePort` implementation was abandoned due to interaction | ||
[problems with Mobile Internet Explorer][IE Problems] in favor of an | ||
implementation backed on the newer and more reliable DOM `MutationObserver` | ||
interface. | ||
These changes were back-ported into this library. | ||
[IE Problems]: https://github.com/cujojs/when/issues/197 | ||
[RSVP ASAP]: https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js | ||
In addition, ASAP factored into `asap` and `asap/raw`, such that `asap` remained | ||
exception-safe, but `asap/raw` provided a tight kernel that could be used for | ||
tasks that guaranteed that they would not throw exceptions. | ||
This core is useful for promise implementations that capture thrown errors in | ||
rejected promises and do not need a second safety net. | ||
At the same time, the exception handling in `asap` was factored into separate | ||
implementations for Node.js and browsers, using the the [Browserify][Browser | ||
Config] `browser` property in `package.json` to instruct browser module loaders | ||
and bundlers, including [Browserify][], [Mr][], and [Mop][], to use the | ||
browser-only implementation. | ||
[Browser Config]: https://gist.github.com/defunctzombie/4339901 | ||
[Browserify]: https://github.com/substack/node-browserify | ||
[Mr]: https://github.com/montagejs/mr | ||
[Mop]: https://github.com/montagejs/mop | ||
## License | ||
Copyright 2009-2013 by Contributors | ||
Copyright 2009-2014 by Contributors | ||
MIT License (enclosed) | ||
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Nonpermissive License
License(Experimental) A package's licensing information has fine-grained problems
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
28654
6
413
2
0
219
0
10
1
1
1
1