From c0626c0baf257506cf1cd9fa95ccf2e8ffd9ef5b Mon Sep 17 00:00:00 2001 From: Eryn Lynn Date: Sat, 28 Sep 2019 17:38:35 -0400 Subject: [PATCH] Make promisify catch errors Closes #12 --- CHANGELOG.md | 2 ++ lib/README.md | 2 ++ lib/init.lua | 10 ++++++++-- lib/init.spec.lua | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2975113..30d1390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Fix issue with Promise.race/all always cancelling instead of only cancelling if the Promise has no other consumers - Make error checking more robust across many methods. - Promise.Status members are now strings instead of symbols, and indexing a non-existent value will error. +- Improve stack traces +- Promise.promisify will now turn errors into rejections even if they occur after a yield. # 2.4.0 diff --git a/lib/README.md b/lib/README.md index cae404b..49e4c27 100644 --- a/lib/README.md +++ b/lib/README.md @@ -129,6 +129,8 @@ docs: desc: | Wraps a function that yields into one that returns a Promise. + Any errors that occur while executing the function will be turned into rejections. + ```lua local sleep = Promise.promisify(wait) diff --git a/lib/init.lua b/lib/init.lua index 2dcb5fd..e3432d6 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -368,10 +368,16 @@ end ]] function Promise.promisify(callback) return function(...) + local traceback = debug.traceback() local length, values = pack(...) - return Promise.new(function(resolve) + return Promise.new(function(resolve, reject) coroutine.wrap(function() - resolve(callback(unpack(values, 1, length))) + local ok, resultLength, resultValues = packResult(pcall(callback, unpack(values, 1, length))) + if ok then + resolve(unpack(resultValues, 1, resultLength)) + else + reject((resultValues[1] or "error") .. "\n" .. traceback) + end end)() end) end diff --git a/lib/init.spec.lua b/lib/init.spec.lua index e33aa01..c8d0d90 100644 --- a/lib/init.spec.lua +++ b/lib/init.spec.lua @@ -624,6 +624,21 @@ return function() expect(status).to.equal(Promise.Status.Resolved) expect(result).to.equal(2) end) + + it("should catch errors after a yield", function() + local bindable = Instance.new("BindableEvent") + local test = Promise.promisify(function () + bindable.Event:Wait() + error('errortext') + end) + + local promise = test() + + expect(promise:getStatus()).to.equal(Promise.Status.Started) + bindable:Fire() + expect(promise:getStatus()).to.equal(Promise.Status.Rejected) + expect(promise._values[1]:find("errortext")).to.be.ok() + end) end) describe("Promise.tap", function()