Promises and Why You Should Use Them

And to everyone who thinks it makes them look smart by spewing vulgarities at me and this post or saying “Why would I want Lua to be like JavaScript”: You’re totally missing the point. The purpose of this library is not to produce some kind of nostalgia for JavaScript by using one of its features in Lua. Promises/Futures/Tasks/etc are present in all of the following languages: C#, Python, Hack, Dart, Kotlin, JavaScript, Scala, Rust, C++, and probably more that I just don’t know about.

The Promise pattern exists in these languages because it solves real problems (that I outlined in the OP). Promises are not only a different way to handle asynchronous code, they provably prevent mistakes and centralize the model so disparate parts of our code can all be on the same page.

It’s not funny, and being so dismissive about ideas that are new to you actively harms yourself and deters others from being open to learn. This idea that Lua should “remain pure” and resist outside influence from other languages is absolutely misguided.

We should all work to build each other up and learn from each other. This kind of attitude is toxic to the community and toxic to yourselves.

62 Likes

Thanks for the thorough answers. Quick question, I see that promise.spawn was removed from the latest version. Am I supposed to use promise.async now to replace my spawns?

Also, am I supposed to use it like this or add a resolve() in the end or something? Thinking it could cause memory leaks.

function Cooldown()
	Promise.async(function()
		Tool.Enabled = false
		wait(0.6)
		Tool.Enabled = true
	end)
end

Check out the Guide. The way you’re using Promise.async isn’t correct. You need to resolve or reject your Promises! I removed Promise.spawn because it was exposing an implementation detail that I decided I didn’t want to encourage people to use outside of in conjunction with Promises. Promise.async always just used Promise.spawn implicitly anyways, so the behavior is still the same.

4 Likes

How would I build a recursive Promise that runs itself when it rejects until it successfully resolves?

1 Like

Promises are eager which means that the instant you create a Promise it’s already running. Thus there’s no way to re-run a Promise per se. To retry the same operation, you’d need to have a new Promise for each attempt. That usually boils down to calling the function multiple times.

Something like this:

local function retry(callback, times, ...)
  local args, length = {...}, select("#", ...)

  return callback(...):catch(function(...)
    if times > 0 then
      return retry(callback, times - 1, unpack(args, 1, length)) 
    else
      return Promise.reject(...)
    end
  end)
end

local function test(value)
	return Promise.new(function(resolve, reject)
		(math.random() >= 0.95 and resolve or reject)(value)
	end)
end


print(retry(test, 10, "hello world"):awaitStatus())
6 Likes

Although, is there’s any cases where Promises were used for any game mechanics just so i can get a clear vision.

1 Like

Hi,

I’m not too experienced with threading so bear with me on this one; in your documentation and various devforum posts you outline how spawn and wait functions aren’t ideal which was helpful. However, in the usage guide wait() is used within a Promise, so, in general, how should wait be used if at all?

1 Like

I don’t think it should be used at all. wait was used there because it’s a simple function that yields and everyone understands.

I’m working on v2 of the Promise library right now and with that I want to write a more comprehensive usage guide that makes things a lot more clear. I’ll make sure to avoid the wait function in that new version to avoid confusion

Promise.delay is how I recommend waiting for some amount of time.

5 Likes

Haven’t fully read the post, so maybe you explain it later on, but when I wrote the first promise example you have given.
(Which is:

local HttpService = game:GetService("HttpService")
local function httpGet(url)
	return Promise.async(function(resolve, reject)
		local ok, result = pcall(HttpService.GetAsync, HttpService, url)

		if ok then
			resolve(result)
		else
			reject(result)
		end
	end)
end

)

I got a warning “Unknown global ‘Promise’”
Can you help me? :slight_smile:

You haven’t defined what “Promise” is.
In OPs code, promise is set to local promise = httpGet("https://google.com")

Please read the full thread before replying next time.

You didn’t require the Promise library, which was omitted from the example code.
If you’ve already placed the Promise library into the game, just put local Promise = require(path.to.Promise) at the top of your code.

2 Likes

What’s the resolve and reject parameter in Promise.new() suppose to be? Am I suppose to pass some kind of resolve and reject argument inside of them? I didn’t see the httpget example pass anything into the Promise.async()

OMG its JavaScript/like promises

1 Like

@evaera, Just want to verify that it is the init.lua file in your repo that is the Promise.lua file we should be using?

1 Like

hey! thanks for this wonderful open source module you posted but for some reason when I try to run this, it says its an unhandled promise rejection. Whats wrong with this code?

– Code

local tabNaruto = {Naruto = 5}

local function promiseCal(callback)

Promise.try(function()

return callback() and “ok” or error(“Oh an error!”)

end)

end

promiseCal(function()

print(tabNaruto.Naruto.shirt)

end):andThen(function(message)

print(message)

end):catch(function(err)

print(string.format(“Error: %s”, err))

end)

The Roblox community has benefited greatly from promises and coroutines. They provide us the tools we need to construct a far more dependable manner of creating code and accessing data. Furthermore, it is open source, which means that anybody may contribute to the community and help keep it up to date.

1 Like

hey. i’m using your module in my roblox game and I have a small problem. When I cancel a running promise, it sends an error “cannot resume dead coroutine”, Idk how to fix this, it doesnt give me any problem but it bother me to check the output or something like this.
Example

local promise = require(...)
local _promise = promise.new(function(resolve, reject, onCancel)
    wait(5)
    print("Resolved")
    resolve()
end)

wait(2)
_promise:cancel() --> cancel the coroutine but after the _promise wait finishes, it gives me an error "cannot resume dead coroutine"

If you could help me with that problem thx!

You added a wait(2) before canceling and by that time your promise is already settled, meaning no point in canceling.
Documentation: Promise | roblox-lua-promise

Yeah Ik I did it to see the difference
The problem isn’t that, the problem is when I’m cancelling a promise which have a wait or which is yielding, I get an error “cannot resume dead coroutine”, it is the same for coroutines

This is because you’re using legacy wait, which doesn’t check whether the thread is dead or not before trying to resume the thread. This causes the error, as promises kill the thread before wait is done yielding. In this case, you can just change wait to task.wait and it should work without outputting the error.

This happens with other things too unfortunately, like with RemoteFunctions, BindableFunctions for example, since they don’t either check whether the thread is dead or not before trying to resume the thread.

Here’s a bug report for it.

1 Like