©2016 - Joshua Sideris
Sequencr.js v6

 ¦

Download Sequencr.js


Features

Function Chaining

Sequencr.js was originally built to solve the annoying callback hell problem encountered when chaining computationally expensive blocking functions together.


This

setTimeout(function(timeout)
{
    function1();
    setTimeout(function(timeout)
    {
        function2();
        setTimeout(function(timeout)
        {
            function3();
        }, timeout, timeout)
    }, timeout, timeout)
}, 10, 10);
					

becomes this.

Sequencr.chain([function1, function2, function3], 10);
					

But this is also handy for adding visual effects to a page. Hint: click Mario to whack him with a boomerang or give him a red mushroom.


function marioBig(){
	$("#mario").css("height", "100px");
}

function marioSmall(){
	$("#mario").css("height", "70px");
}

var changingLock = false;

$(function(){
	$("#mario").click(function(){
		if(changingLock) return;
		changingLock = true;
		if($(this).css("height") == "70px"){
			Sequencr.chain([
				marioBig, 
				marioSmall, 
				marioBig, 
				marioSmall, 
				marioBig, 
				function(){changingLock = false;}
			], 100);
		}
		else{
			Sequencr.chain([
				marioSmall, 
				marioBig, 
				marioSmall, 
				marioBig, 
				marioSmall, 
				function(){changingLock = false;}
			], 100);
		}
	});
});
					

I should mention that jquery is not a dependancy.

Any value returned by a function will be used as an argument to the next function.

The timeout parameter for Sequencr.chain can be a function that returns an integer. This way, you can have dynamic timeouts. If a function is used in place of timeout, that function will be passed a parameter indicating 0-based index of the function in the array. This can be useful for a number of applications. Suppose you wanted to chain several functions with different timeouts. You could do something like this:

Sequencr.chain([function1, function2, function3], function(i){return [100, 400, 500][i];});

Non-Blocking Loops

How do you avoid crashing the browser when running CPU-intensive loops? Since JavaScript doesn't have a native sleep() function, the answer is timeouts, of course. I got fed up with the limitations and annoyances of setInterval, clearInterval, and setTimeout, so I built a better solution: Sequencr.for and Sequencr.do.

This can be useful for breaking up expensive loops. For instance, checking relatively high prime numbers without degrading user experience (yes it's been processing the entire time while you were up there playing with Mario):


function recursivePrimeFinder(value) {
	var sqrt = ~~Math.sqrt(value);
	Sequencr.for(0, sqrt / 1000, 
		function(i){
			for(var x = i * 1000 + 2; x < sqrt && x < value; x++){
				if(value % x === 0) {
					return false; //Breaks out of the loop
				}
			}
		}, 
		0, //Interval
		function(completed){ 
			//Done callback - completed will be true iff value is prime
			if(completed){
				console.log(value);
			}
			recursivePrimeFinder(value + 2);
		});
}

recursivePrimeFinder(10000000001); //Prime numbers > ten billion.
					

    Note that the above example also demonstrates one method of pulling off an asynchronous nested loop, i.e., consider this typical blocking prime finder who's algorithm was used to build the above:

    for(var i = 2; i < 1000; i++){ //Test integers from 1 -> 1000
    	prime = true; 
    	for(j = 2; j < ~~Math.sqrt(i); j++)){ //Check factors
    		if(i % j === 0) {
    			prime = false; 
    			break;
    		}
    	}
    	if(prime)
    		console.log(i);
    }
    				

    Nesting is a bit tricky when the inner loop is required to be asynchronous. Recursion (or flattening the loop) works,
    or use Sequencr.js' built-in promise-based techniques (keep reading).


    Sequencr.js' callback-based loops also provide a succinct solution to numerous other common tasks.


    sampleText = "The quick brown fox jumps over the lazy dog.";
    var printLock = false;
    var timeout = 75;
    function typeText(){
    	if(printLock)
    		return;
    	printLock = true;
    
    	Sequencr.for(0, sampleText.length, function(i){
    		console.log(sampleText[i]); //Print current char
    	}, timeout, //Timeout in ms
    	function(){printLock = false}); //Done callback
    }
    					


    Let's improve the above code - we'll make it more realistic, as if a human were actually typing it. Here's something setInterval can't do: variable timeouts. Just set timeout to a function that returns an integer.
    timeout = function(i){ //i is the index within the loop
    	var timeout = 50; //Min timeout.
    	timeout += 200 * Math.random(); //Random factor
    	if(sampleText[i] == sampleText[i].toUpperCase()){
    		//Everyone knows uppercase letters take longer to type
    		timeout += 25; 
    	}
    	switch(sampleText[i].toLowerCase()){
    		case ".":
    		case ",":
    			timeout += 100 * Math.random(); //Punctuation penalty
    			break;
    		case "a":
    		case "s":
    		case "d":
    		case "f":
    		case "j":
    		case "k":
    		case "l":
    		case ";":
    			timeout += 10 * Math.random(); //Home row penalty
    			break;
    		case "q":
    		case "z":
    		case "p":
    		case "/":
    			timeout += 60 * Math.random(); //Pinky penalty
    			break;
    		default:
    			timeout += 40 * Math.random(); //Other keys penalty
    	}
    	return timeout;
    }
    					


    Promises

    Dealing with code that must make asynchronous requests? You need promises. Sequencr.js has you covered.

    We've got Sequencr.promiseChain, and Sequencr.promiseFor for your enjoyment. Let's take another look at that prime number example from above because if it were written using Sequencr.promiseFor, we wouldn't need to use recursion or do any flattening, and that's just what promises are for!

    //Infinite promise loops are not possible because the promise chain is set up all at once, 
    //but you could always execute this multiple times on different number ranges.
    Sequencr.promiseFor(2, 1000, function(resolve, reject, i){
    	if(i > 3){
    		Sequencr.promiseFor(2, ~~Math.sqrt(i) + 1, function(resolve, reject, j){
    			setTimeout(function(){ //Prevent hanging.
    				if(i % j === 0) 
    					reject(i); //Break out of internal loop (not prime)
    				else
    					resolve(i); //Continue internal loop (could be prime)
    			}, 0);
    		}) //Returns a promise to the last iteration.
    		.then(function(x){
    			console.log(x + " is prime.");
    			resolve(); //Continue
    		}).catch(function(x){
    			//console.log(x + " is not prime.");
    			resolve(); //Continue
    		});	
    	}
    	else{
    		console.log(i + " is prime.");
    		resolve(); //Continue
    	}
    });
    			

      Sequencr.promiseChain is also useful for chaining together functions that make asynchronous requests. For instance:

      var myPromise = Sequencr.promiseChain([
      	function(resolve, reject){
      		resolve(100); //Arbitrary value.
      	}, 
      	function(resolve, reject, x){
      		resolve(x * 2);
      	}, 
      	function(resolve, reject, x){
      		resolve(x + 10);
      	}, 
      ]);
      
      //Whenever/later:
      myPromise.then(function(x){
          console.log(x); //Will print 210, given the above sequence.
      });
      			

      See how Sequencr sets up and manages the actual promise objects? All you need to do is call either resolve or reject when your work is done. Simple. Let's try a more practical example.

      Sequencr.promiseChain([
      	//GET AN ACCESS TOKEN
      	function(resolve, reject){
      		console.log("Getting access token.");
      		$.get("http://jsideris.github.io/Sequencr.js/access_token", 
      			{username: "jsideris", password: "123456789"})
      			.done(function(data){
      				console.log("Access token is: " + data);
      				resolve(data); //Return, pass access token into next function.
      			}).fail(function(data){reject("Failed to get access token.");});
      	}, 
      	//USE THE TOKEN TO ACCESS PRIVATE CONTENT
      	function(resolve, reject, accessToken){
      		$.get("http://jsideris.github.io/Sequencr.js/secret_file?accesstoken=" 
      			+ accessToken)
      			.done(function(data){resolve(data);})
      			.fail(function(data){reject("Failed get secret file.");});
      	}
      ]).then(function(data){
      	//PRINT FILE TO CONSOLE.
      	console.log("Successfully retrieved file:");
      	console.log(data);
      }).catch(function(reason){
      	//ONE OF THE ABOVE ASYNC REQUESTS FAILED.
      	console.log(reason);
      });
      			

      Manual + More Info

      Check out the Sequencr.js wiki on github for more infromation, including the Sequencr.js manual, licensing, and how to contribute.