The library is written in strict Luau and exports generic types: Promise<T...>, AnyPromise, Status, Error, ErrorKind, Executor<T...>, and SignalLike<T...>. See the readme for how far the types follow a chain.
The source moved from lib/init.lua to src/init.luau.
The Wally package now contains only the library, default.project.json, the license, the readme, and the changelog.
Promise.defer now uses task.defer instead of waiting for the next Heartbeat. This has different behavior: while the deferred code will still run at the end of the frame, it runs alongside other Lua wake-ups which moves its position in the frame. Also, if you call Promise.defer from inside of an already-deferred thread/Promise, the code will run after your current thread/Promise is finished, during the same frame, rather than on the next frame.
Promise.delay now wraps task.delay. This shouldn't result in any noticeable behavior changes, but changes in behavior are still possible as we don't have direct control over how Roblox's task scheduler works.
Promise:finally no longer observes a rejection from a Promise. Calling Promise:finally is mostly transparent now.
The Promise returned by Promise:finally resolves or rejects with whatever the parent Promise resolved or rejected with. It will be cancelled if the parent Promise is cancelled.
The value returned from the finally handler is discarded now.
If the value returned from the finally handler is a Promise, we wait for it to resolve, but we do not use its value.
If the value returned from the finally handler is a Promise and it rejects, finally returns the new rejected value.
Promise:finally no longer counts as a consumer of the parent Promise for cancellation purposes. If all consumers are cancelled and the only remaining callbacks are finally handlers, the Promise is now cancelled.
The Promise executor thread is now closed with coroutine.close when the Promise is cancelled.
The Promise executor thread is now closed after the Promise settles (calling resolve or reject).
Callbacks enqueued with andThen and catch are now dequeued if the Promise returned by andThen/catch is cancelled.
Calling andThen or catch on an already-cancelled Promise now returns a cancelled Promise instead of returning a rejected Promise
:await, :expect, and :awaitStatus are no longer backed by BindableEvents, and now use the task library directly, so performance should be better.
Promise:finally no longer observes a rejection from a Promise. Calling Promise:finally is mostly transparent now.
The Promise returned by Promise:finally resolves or rejects with whatever the parent Promise resolved or rejected with. It will be cancelled if the parent Promise is cancelled.
The value returned from the finally handler is discarded now.
If the value returned from the finally handler is a Promise, we wait for it to resolve, but we do not use its value.
If the value returned from the finally handler is a Promise and it rejects, finally returns the new rejected value.
Promise:finally no longer counts as a consumer of the parent Promise for cancellation purposes. If all consumers are cancelled and the only remaining callbacks are finally handlers, the Promise is now cancelled.
The Promise executor thread is now closed with coroutine.close when the Promise is cancelled.
The Promise executor thread is now closed after the Promise settles (calling resolve or reject).
Callbacks enqueued with andThen and catch are now dequeued if the Promise returned by andThen/catch is cancelled.
Calling andThen or catch on an already-cancelled Promise now returns a cancelled Promise instead of returning a rejected Promise
:await, :expect, and :awaitStatus are no longer backed by BindableEvents, and now use the task library directly, so performance should be better.
Runtime errors are now represented by objects. You must call tostring on rejection values before assuming they are strings (this was always good practice, but is required now).
Yielding is now allowed in Promise.new, andThen, and Promise.try executors.
Errors now have much better stack traces due to using xpcall internally instead of pcall.
Stack traces will now be more direct and not include as many internal calls within the Promise library.
Chained promises from resolve() or returning from andThen now have improved rejection messages for debugging.
Promise.async has been renamed to Promise.defer (Promise.async references same function for compatibility)
Promises now have a __tostring metamethod, which returns Promise(Resolved) or whatever the current status is.
Promise:timeout() now rejects with a Promise.Error(Promise.Error.Kind.TimedOut) object. (Formerly rejected with the string "Timed out")
Attaching a handler to a cancelled Promise now rejects with a Promise.Error(Promise.Error.Kind.AlreadyCancelled). (Formerly rejected with the string "Promise is cancelled")
Changed Promise.is to be safe when dealing with tables that have an __index metamethod that creates an error.
Promise.delay resolve value (time passed) is now more accurate (previously passed time based on when we started resuming threads instead of the current time. This is a very minor difference.)