Skip to content

Instantly share code, notes, and snippets.

@toriaezunama
Created April 2, 2012 04:16
Show Gist options
  • Save toriaezunama/2280685 to your computer and use it in GitHub Desktop.
Save toriaezunama/2280685 to your computer and use it in GitHub Desktop.
#lua #deferred #jquery
--[[
== Example use ==
local Deferred = require( 'lib.deferred' )
local unitOfProcessing = function( aDefer )
local closure = function() aDefer:resolve( "hi there" ) end
timer.performWithDelay( 2000, closure )
end
defer = Deferred.new( unitOfProcessing )
-- Uncommenting this should result in the following done() s not being called
--defer:fail( function( aErrMsg )
-- print( "Arrrge we failed with: " .. tostring( aErrMsg ) )
--end )
--defer:reject( "Error occured" )
-- Should be called after 2 seconds
defer:done( function ( aParam1 )
print( 'defer.done() works. ' .. tostring( aParam1 ) )
end);
-- Already resolved in the previous done() (500ms earlier) so should trigger instantly
timer.performWithDelay( 2500, function()
defer:done( function( aParam1 )
print( '(resolved) defer.done() works. ' .. tostring( aParam1 ) )
end )
end );
]]
--== Built in functions
local assert = assert
local error = error
local pairs = pairs
local ipairs = ipairs
local print = print
local setmetatable = setmetatable
local tonumber = tonumber
local tostring = tostring
local type = type
local unpack = unpack
--== Built in lib
local string = string
local table = table
local timer = timer
--== Libs
--local Utils = require( 'lib.utils' )
Utils = {}
Utils.concatArray = function( aSrc, aDst )
for _, v in ipairs( aSrc ) do
aDst[ #aDst + 1 ] = v
end
end
--== Define what we want to be available in the sandbox
local Deferred = {}
setfenv( 1, Deferred )
--== From here down we are in scope
Deferred.pending = "pending"
Deferred.resolved = "resolved"
Deferred.rejected = "rejected"
local mt_deferred = {}
-- Add handlers to be called when the Deferred object is either resolved or rejected.
--deferred.always()
-- Add handlers to be called when the Deferred object is resolved.
--[[
The deferred.done() method accepts one or more arguments, all of which can be either a single function or an array of functions. When the Deferred is resolved, the doneCallbacks are called. Callbacks are executed in the order they were added. Since deferred.done() returns the deferred object, other methods of the deferred object can be chained to this one, including additional .done() methods. When the Deferred is resolved, doneCallbacks are executed using the arguments provided to the resolve or resolveWith method call in the order they were added.
]]
function mt_deferred:done( aDoneCallbacks )
-- Defaults
aDoneCallbacks = aDoneCallbacks or {}
if "function" == type( aDoneCallbacks ) then
aDoneCallbacks = { aDoneCallbacks }
end
assert( "table" == type( aDoneCallbacks ) )
if Deferred.pending == self.state then
Utils.concatArray( aDoneCallbacks, self.doneCallbacks )
elseif Deferred.resolved == self.state then
-- Execute callbacks
for _, callback in ipairs( aDoneCallbacks ) do
callback( unpack( self.resolvedArgs ) )
end
end
return self -- chainable
end
-- Add handlers to be called when the Deferred object is rejected.
function mt_deferred:fail( aRejectCallbacks )
-- Defaults
aRejectCallbacks = aRejectCallbacks or {}
if "function" == type( aRejectCallbacks ) then
aRejectCallbacks = { aRejectCallbacks }
end
assert( "table" == type( aRejectCallbacks ) )
if Deferred.pending == self.state then
Utils.concatArray( aRejectCallbacks, self.rejectCallbacks )
elseif Deferred.resolved == self.state then
-- Execute callbacks
for _, callback in ipairs( aRejectCallbacks ) do
callback( unpack( self.rejectArgs ) )
end
end
return self -- chainable
end
-- Reject a Deferred object and call any failCallbacks with the given args.
function mt_deferred:reject( ... )
if Deferred.pending == self.state then
self.state = Deferred.rejected
self.rejectArgs = arg
-- Execute callbacks
for _, callback in ipairs( self.rejectCallbacks ) do
callback( unpack( arg ) )
end
-- Empty arrays
self.doneCallbacks = {}
self.rejectCallbacks = {}
end
return self -- chainable
end
-- Resolve a Deferred object and call any doneCallbacks with the given args.
function mt_deferred:resolve( ... )
if Deferred.pending == self.state then
self.state = Deferred.resolved
self.resolvedArgs = arg
-- Execute callbacks
for _, callback in ipairs( self.doneCallbacks ) do
callback( unpack( arg ) )
end
-- Empty arrays
self.doneCallbacks = {}
self.rejectCallbacks = {}
end
return self -- chainable
end
-- Determine the current state of a Deferred object.
--[[
"pending": The Deferred object is not yet in a completed state (neither "rejected" nor "resolved").
"resolved": The Deferred object is in the resolved state, meaning that either deferred.resolve() or deferred.resolveWith() has been called for the object and the doneCallbacks have been called (or are in the process of being called).
"rejected": The Deferred object is in the rejected state, meaning that either deferred.reject() or deferred.rejectWith() has been called for the object and the failCallbacks have been called (or are in the process of being called).
This method is primarily useful for debugging to determine, for example, whether a Deferred has already been resolved even though you are inside code that intended to reject it.
]]
function mt_deferred:state()
return self.state
end
function new( aAsyncUnitOfProcessing )
local def = {
state = Deferred.pending,
doneCallbacks = {},
rejectCallbacks = {},
}
setmetatable( def, { __index = mt_deferred } )
-- Start the processing and pass in deferred object so that the unit of processing has access to deferred object's interface
aAsyncUnitOfProcessing( def )
return def
end
return Deferred
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment