Skip to content
This repository has been archived by the owner on Dec 8, 2019. It is now read-only.

Commit

Permalink
Fixed an issue where an error thrown by a scheduled callback stopped …
Browse files Browse the repository at this point in the history
…all future execution of scheduled callbacks.

* Revised `nextTick()` to perform safer next tick scheduling - i.e. where a scheduled callback can throw an Error without affecting other scheduled calls.
* Modified `log()` and `done()` to use this safer `nextTick()` implementation to rethrow errors.
* Moved the optimized CallbackQueue to be an internal class used only by Consequence, where its underlying assumption (that execute() will not throw an error) is valid.
* Cloned additional CallbackQueue performance optimizations from promise.coffee and Deft JS - ensuring that only a single Array instance is used, rather than allocating new Arrays (via push(), etc.) whenever new callbacks are added.

Fixes CC-Archived#29
(cherry picked from commit a33c977)
  • Loading branch information
John Yanarella authored and karfau committed Feb 3, 2014
1 parent d7fe1ec commit 362b96a
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 130 deletions.
151 changes: 149 additions & 2 deletions src/com/codecatalyst/promise/Consequence.as
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

package com.codecatalyst.promise
{
import com.codecatalyst.util.nextTick;
import com.codecatalyst.util.optionally;

/**
Expand Down Expand Up @@ -139,7 +138,7 @@ package com.codecatalyst.promise
{
if ( callback is Function )
{
nextTick( transform, [ value, callback ] );
schedule( transform, [ value, callback ] );
}
else
{
Expand All @@ -165,5 +164,153 @@ package com.codecatalyst.promise
resolver.reject( error );
}
}

/**
* Schedules the specified callback function to be executed on
* the next turn of the event loop.
*
* @param callback Callback function.
* @param parameters Optional parameters to pass to the callback function.
*/
private function schedule( callback:Function, parameters:Array = null ):void
{
CallbackQueue.instance.schedule( callback, parameters );
}
}
}

import flash.utils.clearInterval;
import flash.utils.setInterval;

/**
* Used to queue callbacks for execution on the next turn of the event loop (using a single Array instance and timer).
*
* @private
*/
class CallbackQueue
{
// ========================================
// Public properties
// ========================================

/**
* Singleton instance accessor.
*/
public static const instance:CallbackQueue = new CallbackQueue();

// ========================================
// Protected properties
// ========================================

/**
* Queued Callback(s).
*/
protected const queuedCallbacks:Array = new Array(1e4);

/**
* Interval identifier.
*/
protected var intervalId:int = 0;

/**
* # of pending callbacks.
*/
protected var queuedCallbackCount:uint = 0;

// ========================================
// Constructor
// ========================================

public function CallbackQueue()
{
super();
}

// ========================================
// Public methods
// ========================================

/**
* Add a callback to the end of the queue, to be executed on the next turn of the event loop.
*
* @param callback Callback function.
* @param parameters Optional parameters to pass to the callback function.
*/
public function schedule( callback:Function, parameters:Array = null ):void
{
queuedCallbacks[ queuedCallbackCount++ ] = new Callback( callback, parameters );

if ( queuedCallbackCount == 1 )
{
intervalId = setInterval( execute, 0 );
}
}

// ========================================
// Protected methods
// ========================================

/**
* Execute any queued callbacks and clear the queue.
*/
protected function execute():void
{
clearInterval( intervalId );

var index:uint = 0;
while ( index < queuedCallbackCount )
{
(queuedCallbacks[ index ] as Callback).execute();
queuedCallbacks[ index ] = null;
index++;
}

queuedCallbackCount = 0;
}
}

/**
* Used to capture a callback closure, along with optional parameters.
*
* @private
*/
class Callback
{
// ========================================
// Protected properties
// ========================================

/**
* Callback closure.
*/
protected var closure:Function;

/**
* Callback parameters.
*/
protected var parameters:Array;

// ========================================
// Constructor
// ========================================

public function Callback( closure:Function, parameters:Array = null )
{
super();

this.closure = closure;
this.parameters = parameters;
}

// ========================================
// Public methods
// ========================================

/**
* Execute this callback.
*/
public function execute():void
{
closure.apply( null, parameters );
}
}
135 changes: 7 additions & 128 deletions src/com/codecatalyst/util/nextTick.as
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

package com.codecatalyst.util
{
import flash.utils.clearInterval;
import flash.utils.setInterval;

/**
* Executes the specified callback function on the next turn of the event loop.
*
Expand All @@ -30,134 +33,10 @@ package com.codecatalyst.util
*/
public function nextTick( callback:Function, parameters:Array = null ):void
{
CallbackQueue.instance.schedule( callback, parameters );
}
}

import flash.utils.clearInterval;
import flash.utils.setInterval;

/**
* Used to queue callbacks for execution on the next turn of the event loop (using a single timer).
*
* @private
*/
class CallbackQueue
{
// ========================================
// Public properties
// ========================================

/**
* Singleton instance accessor.
*/
public static const instance:CallbackQueue = new CallbackQueue();

// ========================================
// Protected properties
// ========================================

/**
* Queued Callback(s).
*/
protected const queuedCallbacks:Array = [];

/**
* Interval identifier.
*/
protected var intervalId:int = 0;

// ========================================
// Constructor
// ========================================

public function CallbackQueue()
{
super();
}

// ========================================
// Public methods
// ========================================

/**
* Add a callback to the end of the queue, to be executed on the next turn of the event loop.
*
* @param callback Callback function.
* @param parameters Optional parameters to pass to the callback function.
*/
public function schedule( callback:Function, parameters:Array = null ):void
{
queuedCallbacks.push( new Callback( callback, parameters ) );

if ( queuedCallbacks.length == 1 )
{
intervalId = setInterval( execute, 0 );
function execute():void {
clearInterval( intervalId );
callback.apply( null, parameters );
}
}

// ========================================
// Protected methods
// ========================================

/**
* Execute any queued callbacks and clear the queue.
*/
protected function execute():void
{
clearInterval( intervalId );

for each ( var queuedCallback:Callback in queuedCallbacks )
{
queuedCallback.execute();
}

queuedCallbacks.length = 0;
var intervalId:int = setInterval( execute, 0 );
}
}

/**
* Used to capture a callback closure, along with optional parameters.
*
* @private
*/
class Callback
{
// ========================================
// Protected properties
// ========================================

/**
* Callback closure.
*/
protected var closure:Function;

/**
* Callback parameters.
*/
protected var parameters:Array;

// ========================================
// Constructor
// ========================================

public function Callback( closure:Function, parameters:Array = null )
{
super();

this.closure = closure;
this.parameters = parameters;
}

// ========================================
// Public methods
// ========================================

/**
* Execute this callback.
*/
public function execute():void
{
closure.apply( null, parameters );
}
}

0 comments on commit 362b96a

Please sign in to comment.