can-wait

0.3.2 • Public • Published

Build Status npm version

can-wait

A library that tracks asynchronous activity and lets you know when it has completed. Useful when you need to call a function and wait for all async behavior to complete, such as when performing rendering.

Install

npm install can-wait --save

Usage

var Zone = require("can-wait");

new Zone().run(function(){

	setTimeout(function(){
		
	}, 29);

	setTimeout(function(){
		
	}, 13);

	var xhr = new XMLHttpRequest();
	xhr.open("GET", "http://chat.donejs.com/api/messages");
	xhr.onload = function(){
		
	};
	xhr.send();

}).then(function(){
	// All done!
});

Tasks

JavaScript uses various task queues (and a microtask queue) to run JavaScript in the event loop. See this article and this StackOverflow answer to learn more.

For can-wait to work we have to override various task-creating functionality, this is the list of what we currently implement:

Macrotasks

  • setTimeout
  • XMLHttpRequest

Microtasks

  • requestAnimationFrame
  • Promise
  • process.nextTick

API

In Node there are various async methods that do not fall into the most common macrotasks, but still might need to be tracked. To accomodate this we expose a global Zone.waitFor function that can be used to wait on the outcome of an asynchronous request.

var Zone = require("can-wait");
var fs = require("fs");

fs.readFile("some/file", Zone.waitFor(function(err, file){
	// We've waited
}));

Zone.current

Represents the currently running zone. If the code using Zone.current is not running within a zone the value will be undefined.

var Zone = require("can-wait");

var myZone = new Zone();

myZone.run(function(){

	Zone.current === myZone;

});

Zone.waitFor

Zone.waitFor is a function that creates a callback that can be used with any async functionality. Calling Zone.waitFor registers a wait with the currently running request and returns a function that, when called, will decrement the wait count.

This is useful if there is async functionality other than what we implement. You might be using a library that has C++ bindings and doesn't go through the normal JavaScript async APIs.

var Zone = require("can-wait");
var asyncThing = require("some-module-that-does-secret-async-stuff");

asyncThing(Zone.waitFor(function(){
	// We waited on this!
}));

Zone.prototype.data

You might want to get data back from can-wait, for example if you are using the library to track asynchronous rendering requests. Each zone contains a data object which can be used to store artibitrary values.

var Zone = require("can-wait");

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com");
xhr.onload = function(){
	// Save this data for later
	Zone.current.data.xhr = xhr.responseText;
};
xhr.send();

Zone.error

Allows you to add an error to the currently running zone.

var Zone = require("can-wait");

new Zone().run(function(){

	setTimeout(function(){
		Zone.error(new Error("oh no"));
	}, 100);

}).then(null, function(error){
	error; // -> {message: "oh no"}
});

Zone.ignore

Creates a function that, when called, will not track any calls. This might be needed if you are calling code that does unusual things, like using setTimeout recursively indefinitely.

var Zone = require("can-wait");

new Zone().run(function(){
	function recursive(){
		setTimeout(function(){
			recursive();
		}, 20000);
	}

	var fn = Zone.ignore(recursive);

	// This call will not be waited on.
	fn();
});

ZoneSpec

Each zone you create takes a ZoneSpec that defines behaviors that will be added to the zone. A common use case is to provide globals that you want to add within the zone. Common globals such as document, window, and location can be placed directly on the zoneSpec, all others within the globals object.

var Zone = require("can-wait");

var zone = new Zone({
	document: document,
	globals: {
		foo: "bar"
	}
});

The ZoneSpec can be provided as an object (like above) or a function that returns a ZoneSpec:

var Zone = require("can-wait");

var zone = new Zone(function(){
	var foo = "bar";
	return {
		beforeTask: function(){
			global.foo = foo;
		}
	}
});

Plugins provide a way to inherit behavior defined in other ZoneSpecs. Here's a plugin that changes the title of your page randomly.

var titleZone = function(){
	return {
		beforeTask: function(){
			document.title = Math.random() + " huzzah!";
		}
	}
};

var zone = new Zone({
	plugins: [titleZone]
});

Since plugins are also defined with ZoneSpecs (or functions that return ZoneSpecs) this means you can have plugins that use plugins, that use plugins... We hope that there will be developed "bundles" of plugins that provide robust behaviors.

The ZoneSpec defines the following hooks:

created

Called when the zone is first created, after all ZoneSpecs have been parsed. this is useful if you need to do setup behavior that covers the entire zone lifecycle.

beforeTask

Called before each Task is called. Use this to override any globals you want to exist during the execution of the task:

new Zone({
	beforeTask: function(){
		window.setTimeout = mySpecialSetTimeout;
	}
});

afterTask

Called after each Task is complete. Use this to restore state that was replaced in beforTask:

var oldSetTimeout;

new Zone({
	beforeTask: function(){
		oldSetTimeout = window.setTimeout;
		window.setTimeout = mySpecialSetTimeout;
	},
	afterTask: function(){
		window.setTimeout = oldSetTimeout;
	}
});

ended

Called when the Zone has ended and is about to exit (it's Promise will resolve).

License

MIT

Package Sidebar

Install

npm i can-wait

Weekly Downloads

1

Version

0.3.2

License

MIT

Last publish

Collaborators

  • justinbmeyer
  • matthewp