I made small and simple implementation of promises. It wraps all the functions in a coroutine and then makes the returned value available to the next next/catch/await (“then” is named “next” because I can’t use a keyword as a key but I can use a global).
They aren’t allowed to be directly accessed through the object because of my __index function. It also wraps the functions in another function so I can access them using a . instead of a : (which looks better while chaining imo)
Is how my implementation functions correct/can I make it more efficient in any way?
local Promise = {}
Promise.__index = function(self, key)
if key == "new" then return Promise[key] end
return function(handler)
return Promise[key](self, handler)
end
end
function Promise.new(func)
local self = setmetatable({}, Promise)
coroutine.wrap(func)(
function(value) -- resolve
self._returned = value
end,
function(value) -- reject
self._error = value
end
)
return self
end
function Promise:await()
return self._returned or self._error
end
function Promise:next(func)
local success, result = pcall(function()
return coroutine.wrap(func)(self._returned)
end)
return self.new(function(resolve, reject)
(success and resolve or reject)(result)
end)
end
function Promise:catch(func)
return coroutine.wrap(func)(self._error)
end
return Promise
--// thanks for stopping by
Here is how I tested it:
--// test 1
local Promise = require(path.to.promises)
Promise.new(function(resolve, reject)
resolve("y")
end)
.next(function(value)
return value .. "es"
end)
.next(function(value)
print(value) --> yes
return value .. 3
end)
.catch(function(err)
print(err) --> attempt to concat number and string
end)
--// test 2
local Promise = require(path.to.promises)
local result = Promise.new(function(resolve, reject)
resolve(4)
end).await()
print(result) --> 4
I can’t quite write a full review as I’m on the move at the moment, but I can address a few concerns:
Yes, using : may not be as beautiful as you might like, but the Luau VM optimizes method calls and this codebase is not idiomatic of Lua or Luau. To put it simply, you’re going through loops to confuse any end-user at a later date, stripping away performance, and making a less-maintainable option which, well, I definitely don’t think is a plus.
Moving right along, I’m seeing a lot of metaprogramming. Functional uses of metatables for things besides prototype-based object-oriented programming. Also, your namespaces for the static side of Promise and the instance side are a bit muddled through new. These changes are pretty needless and seem to be traps for the future.
So, yes, this minimal implementation works but a quick refactor may not hurt it a bit.
Just declare the Promise.new function and don’t use self.new to refer to Promise.new. The naming resolution you had before, where a static and instance function have the same resolution achieved through a metatable, is not commonplace for any programming language I’ve seen before.
Can you show what you meant by the first and fourth points? I don’t know how to implement it.
Here’s my updated version:
local Promise = {}
Promise.__index = Promise
local function wrapPcall(...)
local result = {pcall(...)}
return result[1], table.remove(result, 1)
end
function Promise.new(func, chained)
local self = setmetatable({
_Chained = chained or {}
}, Promise)
coroutine.wrap(func)(
function(...) -- resolve
self._Returned = {...}
end,
function(...) -- reject
self._Returned = {...}
end
)
table.insert(self._Chained, self)
return self
end
function Promise:await()
return table.unpack(self._Returned)
end
function Promise:next(func)
local success, results = wrapPcall(coroutine.wrap(func), self._Returned)
return Promise.new(function(resolve, reject)
(success and resolve or reject)(table.unpack(results))
end, self._Chained)
end
function Promise:catch(func)
local success, results = wrapPcall(coroutine.wrap(func), self._Returned)
return Promise.new(function(resolve, reject)
(success and resolve or reject)(table.unpack(results))
end, self._Chained)
end
function Promise:finally(func)
return coroutine.wrap(func)(self:await())
end
return Promise
--// thanks for stopping by