Git Product home page Git Product logo

asap's Introduction

ASAP

Build Status

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.) 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.

asap(function () {
    // ...
});

This CommonJS package provides an asap module that exports a function that executes a task function as soon as possible.

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.

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.

function loop() {
    asap(loop);
}
loop();

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").

Raw ASAP

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.

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.

Compatibility

ASAP is tested on Node.js v0.10 and in a broad spectrum of web browsers. The following charts capture the browser test results for the most recent release. The first chart shows test results for ASAP running in the main window context. The second chart shows test results for ASAP running in a web worker context. Test results are inconclusive (grey) on browsers that do not support web workers. These data are captured automatically by Continuous Integration.

Browser Compatibility

Compatibility in Web Workers

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:

  • 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 absence 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.

  • Internet Explorer 10
  • Safari 5.0-1
  • Opera 11-12

In the absence 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

ASAP has been factored out of the Q asynchronous promise library. It originally had a naïve implementation in terms of setTimeout, but Malte Ubl provided an insight that postMessage might be useful for creating a high-priority, no-delay event dispatch hack. Since then, Internet Explorer proposed and implemented setImmediate. 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 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.

For further reading, Nicholas Zakas provided a thorough article on The Case for setImmediate.

Ember’s RSVP promise implementation later adopted the name ASAP but further developed the implentation. Particularly, The MessagePort implementation was abandoned due to interaction problems with Mobile Internet Explorer in favor of an implementation backed on the newer and more reliable DOM MutationObserver interface. These changes were back-ported into this library.

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 property in package.json to instruct browser module loaders and bundlers, including Browserify, Mr, and Mop, to use the browser-only implementation.

License

Copyright 2009-2014 by Contributors MIT License (enclosed)

asap's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asap's Issues

Do not assume browserify

Would it be possible not to assume the use of browserify? It would just mean:

  • Not assuming global is available
  • Using a UMD pattern

Thanks!

Testing

How we are going to test asap?

Unit tests seems obvious. Which framework? Mocha?

Beside own tests, should we automatize testing against Q using npm link?

asap uses timer, not MutationObserver, in Safari

asap does not use makeRequestCallFromMutationObserver because if (typeof BrowserMutationObserver === "function") fails: typeof MutationObserver returns "object" in Safari (observed in versions 7-9).

Object.prototype.toString.call(MutationObserver) returns "[object MutationObserverConstructor]".

react-native throw Error when importing asap

react-native throws

Unable to resolve module domain from /Users/almas/Projects/Demo/node_modules/asap/raw.js: Unable to find this module in its module map or any of the node_modules directories under /Users/node_modules/domain and its parent directories

I see that asap generated for react-native differs from that in a browser: raw.js has this line of code in it: require('domain'), which throws.

Consider using Promise.prototype.then

There's a blog post about how using Promise.then is supposedly even faster than .nextTick.

Given that we have stuff like io.js with Promise support and a lot of browser supporting it to, do you think it'd make sense to conditionally use this feature if it exists?

Tests failing in Node 0.8

[1] Prior to Node 0.9 (prior to nodejs/node-v0.x-archive@4401bb4 in particular), domains, intentionally or not, are not adequate for error recovering, but only to perform some synchronous operations before exiting the process (unless you don't exit manually all domains..)

[2] With a proposed fix in #12 (emitting errors instead of throwing them), for some reason, tests are passing in node 0.8 too. However, such errors do not bubble all the way up, avoiding essential logic on how errors are handled before and inside error handlers. Because of that we can not use such approach.

However, [2] suggests that a fix is possible regardless of [1], and at this moment, my first suspect is a problematic situation of multiple domains (probably because of [1]), provoked by mocha in combination with our tests.

I have intention to investigate further this issue, but I am in big preparations before moving to another country for few years, so I am not sure when I will have time for this. In any case, any help is welcome.

Browserify Support

I'm trying to figure out how to bundle the browser version when requiring and bundling asap from another module. It seems that simply require("asap") bundles the node version in the browserify bundle.

I might be missing something trivial, if so, please let me know.

Edit:

It also works if I modify package.json in asap to qualify the browser*js files in the browser section. This seems to be the better option as it requires no change to the package.json in the requiring module.

  "main": "./asap.js",
  "browser": {
    "./asap.js": "./browser-asap.js",
    "./raw.js": "./browser-raw.js",
    "test/domain": "./test/browser-domain.js"
  },

/Edit

It does seem to work if I do the following in package.json in my module that requires asap:

"browser": {
   "asap": "asap/browser-asap"
}

and also manually modifying browser-asap.js in the asap module to require browser-raw

var var rawAsap = require("./browser-raw");

Thanks.

Contributors?

Do you have a contributors file with the names of everyone?
Only reason why I ask is that your Copyright right statement in the license file implies that there is one.
Thanks.

Publish to Bower

@kriskowal I noticed you were reluctant to publish to Component. Do you feel the same way about Bower? It'd be very useful to those relying on it for CI.

What are the tests testing?

The first few I was able to figure out. But I have no idea what these do:

runCase("errors at end", [[e], e], e );
runCase("recursion", [R] );
runCase("recursion with errors", [R, e] );
runCase("multiple recursions", [R], [R], [R, e] );
runCase("recursion - mixed", [R, [], e] );
runCase("recursion - mixed 2", [R, [[[[], e]]], e] );

What kind of asap calls do they create, and what kind of results do they expect?

New Release

It's been a long time since we last had a release. It would be good to get something a bit more up to date.

Local in-browser testing

Since I don't have the credentials.json file, I was unable to publish to S3 for testing.
So I tried to test in my browser.
I browserify-ed asap-test.js, linked it in a HTML file, and opened the page.
There are 6 failing tests.
Also, at the end I have this error: Uncaught SyntaxError: Failed to execute 'postMessage' on 'Window': Invalid target origin '' in a call to 'postMessage'. ("scafold.js" at line 39)

Didn't have more time to investigate.
Am I doing something wrong?

use seperate async polyfill

similar in idea to #32 but instead of breaking asap-raw into a separate file, you could use immediate which seems to cover similar ground as the stuff you were going to put into asap-raw, just async stuff with no error handling.

this would also take care of #1 and #38.

Errors can pass unreported (IE 10)

This is because we are throwing using setImmediate when available, which ends to be unreliable in IE 10.
In those brwsers setTimeout is not always FIFO, and in some others doesn't always fire.

A possible solution could be to queue errors, and to throw them using a requestErrorThrow generated by (a slightly modified) makeRequestFlushFromTimer.
This would result in errors reliably reported in correct order.
Also we would finally get rid off of setImmediate in browsers.

I could open a PR today.

Publish new version soon

Now that ASAP is tested and stable, I think we should publish a new version very soon, since we added some crucial changes since version 1.0.0 (proper support for browserify and domains.)

Can we list here things that we should do before a next version?

  • Travis build status badge

Microtask queue can be made faster

I have created a jsperf for queues. drain_Array is a naive Array.prototype.shift, drain_ArrayQueue is asap's optimization, and drain_Queue uses an implementation of Queues that has O(1) push and amortized O(1) pull.

With my machine, using Google Chrome, asap's optimization for array removal ends up being significantly slower than a naive shift! It seems likely that Chrome already optimizes shift (possibly using a similar technique that asap uses).

The Queue implementation is roughly the same speed as naive shift, but it should be consistently fast on all browsers (even those that don't optimize shift).

Of course, it is possible that I messed up in making the test, and that the results are incorrect.

Usability in development mode with thrown errors

Evidently, at least in recent Chrome, errors that are captured and released asynchronously lose their stack traces.

Options to mitigate this would include at least:

  1. rework how we deal with errors, allowing errors to pass through synchronously after we request another flush, at least in development mode if not in production as well, at the expense of the performance and priority of flushing a queue with uncaught exceptions.
  2. find a way to capture the stack trace on the error before deferring. preliminary attempts failed.

cc @rkatic

Browser field contains invalid route in npm

In 2.0, the browser field was updated to route "./test/domain.js": "./test/browser-domain.js", and the test folder was added to npmignore, thus generating an invalid route when asap is installed via npm.

This causes issues with module resolvers that use the browser field to route requires. Browserify works, since browserify requires the browser routes to be absolute paths first, and it (probably) does not error out on route not found (you can have a billion invalid routes, he doesn't care). QuickStart on the other hand, resolves routes left and right (key & value).

Short explanation: If the browser field specifies "./folder": "./some-route" QuickStart tries to do things right and (recursively) resolve the browser field keys, following the browser field spec. It resolves "./folder" first, because "./folder" could be resolved to "./folder/index.js" or "./folder.js", or even be routed itself using another entry in the browser field. So for every require() every key of the browser field needs to be resolved to see if the result matches the require resolved result, no matter the initial value of the key. And since the npm published version of asap does not contain the /test/ folder, the browser key for "./test/domain.js" is unresolvable, therefore generating an invalid route error.

Simplest solution would be to add back tests to npm. In alternative I suppose it would be relatively easy to implement allowing any number of invalid routes in the module resolver code, though not exactly "correct".

Cross library interleaving can cause node 10's nextTick limit to be hit.

In node 0.10.x Cross library async interleaving can result in (node) warning: Recursive process.nextTick detected. This will break in the next version of node. Please use setImmediate for recursive deferral.

Although, asap typically avoids this with its own micro task queue:

https://github.com/kriskowal/asap/blob/master/raw.js#L86-L96

In some scenarios, it is possible to interleave two such micro tasks queues in a way that still results in the limit being hit.

In addition to interleaving between different libraries due to the node_module's dupe friendly module resolution strategy, it is common to have multiple copies of the same library present and interacting with one another.

A quick example:

var A = require('./path/to/rsvp_a').Promise;
var B = require('./path/to/rsvp_b').Promise;

A.resolve().then(function() {
  console.log('first nextTick');
  B.resolve().then(function() {
    console.log('second nextTick');
    A.resolve().then(function() {
      console.log('third nextTick');
    });
  });
});

An example failing test thanks to @taras https://github.com/tildeio/rsvp.js/pull/337/files#diff-e7e77ddad631a023d39a62f3ba8b7f17R2524

this limit has been removed in node 0.11.0

nodejs/node@0761c90
nodejs/node@0761c90

Unfortunately our solution was to fallback to setImmediate in node 0.10.x.

I wasn't able to think of a better solution, but that doesn't mean a better one doesn't exist so if someone has one feel free to share :)

related:
tildeio/rsvp.js#337
cujojs/when#410
petkaantonov/bluebird#399

asap queue can be effectively overflown (using [email protected])

Minimal example snippet:

"use strict";

var q = require("q");

var i;
var _handle = function (value) {
    console.log(value);
    return value;
};
for (i = 0; i < 1026; i++) {
    q(i).then(_handle).then(_handle);
}

Result (on my machine, 1026 loop iteration fails. I do not know if this is machine dependent)

/opt/socketeer/source/node_modules/q/node_modules/asap/asap.js:40
        this.task.call();
                  ^
TypeError: Cannot call method 'call' of null
    at RawTask.call (/opt/socketeer/source/node_modules/q/node_modules/asap/asap.js:40:19)
    at flush (/opt/socketeer/source/node_modules/q/node_modules/asap/raw.js:50:29)
    at process._tickCallback (node.js:415:13)

node version: v0.10.25

Allow queue to temporarily lower priority

I have a use case where I'm using asap to iterate over a long running (but not infinite!) loop. I'd like to deprioritize the queue to allow other tasks to run (UX events, for example).

I can schedule this myself, by calling nextTick or setTimeout instead of asap occasionally, but it seemed like something this project would be interested in considering.

function to change rawAsap.capacity value must be added

When I handle thousands of promises at the same time I'm getting an error from asap module :
asap\asap.js:40
this.task.call();
^
TypeError: Cannot read property 'call' of null

If i increase the "capacity" value in rawAsap module - errors are gone.
So having a Function for changing asap capacity will be useful.

Chrome + postMessage = slow

at least in it our case, Both Chrome Desktop( Windows, Linux ) and android experience slow down.

We have HIGH memory usage, and very frequents call to setImmediate.
the polifill triggered MinorGC it turns slowing it down. ( i think )

safari , isn't affected some how.

Fatality of uncaught exceptions in NodeJS

The paragraph about uncaught exceptions in the README.md says:

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.

It explains correctly the behavior in browsers, but it is not true in NodeJS. In NodeJS, uncaught exceptions are fatal (exits the process immediately), unless domains (or the "uncaoughtException" event) are used. ASAP, currently follows such "error policy".

Now, we have two options: (1) to fix the paragraph in the README.md, or (2) to make ASAP behave in NodeJS exactly as in browsers, "braking" the "error policy" of NodeJS.

Spectrum browser testing.

I have a proof of concept for using S3 and Sauce Labs / Selenium WebDriver to verify ASAP in a broad spectrum of browsers. This solution will need to be combined with Istanbul to get a better idea of which code paths are being covered by each engine.

Consider supporting a public `asap.flush()` method

From Ward on Stack Overflow, http://stackoverflow.com/questions/19578549/how-do-i-simulate-a-tick-when-testing-components-with-embedded-q-js-promises

I’m intrigued but cautious of the idea. This would break the invariant that only platform code would be on the stack, and would invite an attacker to be able to capture errors thrown by code depending on that invariant. However, Q at least, sans bugs, ensures that errors will not bubble out of ASAP. Ward mentions that it would be useful for writing synchronous tests. I would encourage writing asynchronous tests for asynchronous subsystems, however, we could provide this feature. What is the compelling argument not to?

Does asap have an ECCN?

Hello. Our team would like to use/include your library in our product, and since we export outside of the U.S., we're required to provide ECCN (Export Control Classification Number) information for all 3rd-party libraries that may use/include encryption. We noticed some references to encryption in the credentials functionality, so we thought we should ask. Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.