Promise.lua is a Promises/A+ library written in Lua, against the v1.1 Promises spec.
Promises represent the result of an operation which will complete in the future. They can be passed around, chained onto, and can help to flatten out deeply nested callback code, and simplify error handling to some degree.
There are many, and much better, introductions to promise patterns.
Feel free to send a pull request with improved documentation, and/or references.
Add the promise.lua file to your application, and require it.
local Promise = require('promise')
Promises are resolved asynchronously, and require either a callback akin to javascript's setTimeout, or a periodic call to
Promise.update()
. This can be accomplished in a couple of ways.
When using an event loop, like lua-ev, you can set Promise.async
to a function which will schedule a callback for a later time.
Promise.async = function(callback)
my_ev_loop.create_timer(0, callback)
end
More simply, with an existing game loop, you can call Promise.update()
periodically:
function love.update(dt)
Promise.update()
-- ...
end
local Promise = require('promise')
local promise = Promise.new()
promise:next(function(result)
print('promise resolved', result)
end, function(reason)
print('promise was rejected', reason)
end)
Promises may be chained repeatedly.
local promise = Promise.new()
promise:next(function(result)
print('result1')
end)
local promise2 = promise:next(function(result)
print('result2')
end)
promise2:next(function(result)
print('I only run after promise2 resolves')
end)
local memoizedPromise
local fetchJson = function()
if not memoizedPromise then
memoizedPromise = Promise.new()
fetchJsonEventually(function(json)
promise:resolve(json)
end)
end
return memoizedPromise
end
fetchJson:next(function(json)
return JSON.parse(json)
end):next(function(parsed_json)
return parsed_json.specific_value
end):next(function(specific_value)
print('got specific value')
end):catch(function(reason)
print('an error occurred while fetching the value', reason)
end)
If an error occurs in an onFulfilled callback, that promise will be rejected with the value of the error.
local promise = Promise.new()
promise:next(function()
error('something bad happened!')
):catch(function(reason)
print(reason)
end)
These errors may be caught, and handled in the callback chain:
local promise = Promise.new()
promise:next(function()
return fetchValueWhichCausesAnError()
):catch(function(reason)
print(reason)
return myDefaultValue
end):next(function(result)
print('"result" is either the value returned from fetchValue call, or the myDefaultValue if that threw an error')
end)
Creates a new promise which may later be resolved or rejected.
local promise = Promise.new()
promise:next(callback):next(callback):catch(error_handler)
Returns a new promise, adding it to the current promise's chain.
onFulfilled
will be called when the promise resolves. onRejected
will be called when the promise is rejected.
Both arguments are optional.
local promise = Promise.new()
promise:next(function(result)
print('this callback was successful and returned', result)
end, function(reason)
print('this promise was rejected because', reason)
end)
Alias for promise.next(nil, onRejected)
Resolves the promise with value
Rejects the promise with reason
Creates a promise which resolves after all of its promises resolve, or rejects when any of its promises fail.
local promise1 = Promise.new()
local promise2 = Promise.new()
local promise3 = Promise.new()
Promise.all(promise1, promise2, promise3):next(function(results)
local result1, result2, result3 = unpack(results)
print('All promises have resolved')
end, function(results)
local result1, result2, result3 = unpack(results)
print('At least one promise was rejected')
end)
-- some time later
promise1:resolve(1)
promise2:resolve(2)
promise3:resolve(3)
Creates a promise which resolves as soon as any of its promises resolve, or rejects when all provided promises fail.
local promise1 = Promise.new()
local promise2 = Promise.new()
Promise.race(promise1, promise2):next(function(value)
print('The first promise to resolve returned ' .. value)
end, function(results)
local result1, result2 = unpack(results)
print('All promises were rejected')
end)
-- some time later
promise2:resolve('I resolved first!')
- 1.2
then
is a reserved word in Lua.next
is used instead in this library. - 1.3 Valid value types
-
Promises cannot be resolved with null (
nil
). Lua does not distinguish betweenfunction() return nil end
and
function() return end
-
Lua does not have an
undefined
type.
-
- 2.2.5 Lua method calls do not have a
this
equivalent. Theself
syntactic sugar forself
is determined by method arguments. - 2.3.1 Lua does not have an error type. Specifications calling for
TypeError
will receive a string message beginning withTypeError:
- 2.3.3.3 Lua method calls do not have a
this
equivalent.next
will be called with the first argument ofx
instead.
# Install luarocks
sudo apt-get install luarocks
# Install luasec (required to install specific version of busted)
sudo luarocks install luasec OPENSSL_LIBDIR=/usr/lib/x86_64-linux-gnu/
# Install libev, if not already installed
sudo apt-get install libev-dev
# Install lua-ev
luarocks install lua-ev scm --server=http://luarocks.org/repositories/rocks-scm/
# Install busted
sudo luarocks install busted 1.11.1-2
# Run busted
busted spec
Missing one? Send a pull request!