Skip to content

Commit

Permalink
Promise resolve and reject now return Interrupt errors
Browse files Browse the repository at this point in the history
This is so that it can be handled at all.

Previously the error was just not propagated which lead to interrupts
not really working with asynchronous code.

Fixes dop251#623
  • Loading branch information
mstoykov committed Oct 23, 2024
1 parent 5f46f27 commit 85fc80e
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 4 deletions.
11 changes: 7 additions & 4 deletions builtin_promise.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,14 +595,17 @@ func (r *Runtime) getPromise() *Object {
return ret
}

func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) {
func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) error {
f, _ := AssertFunction(fObj)
return func(x interface{}) {
_, _ = f(nil, r.ToValue(x))
return func(x interface{}) error {
_, err := f(nil, r.ToValue(x))
return err
}
}

// NewPromise creates and returns a Promise and resolving functions for it.
// The returned errors will be Interrupt errors that should be propagated upwards.
// Exceptions are handle through [PromiseRejectionTracker].
//
// WARNING: The returned values are not goroutine-safe and must not be called in parallel with VM running.
// In order to make use of this method you need an event loop such as the one in goja_nodejs (https://github.com/dop251/goja_nodejs)
Expand All @@ -621,7 +624,7 @@ func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) {
// })
// }()
// }
func (r *Runtime) NewPromise() (promise *Promise, resolve func(result interface{}), reject func(reason interface{})) {
func (r *Runtime) NewPromise() (promise *Promise, resolve, reject func(reason interface{}) error) {
p := r.newPromise(r.getPromisePrototype())
resolveF, rejectF := p.createResolvingFunctions()
return p, r.wrapPromiseReaction(resolveF), r.wrapPromiseReaction(rejectF)
Expand Down
37 changes: 37 additions & 0 deletions runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,43 @@ func TestInterruptInWrappedFunctionExpectStackOverflowError(t *testing.T) {
}
}

func TestInterruptWithPromises(t *testing.T) {
rt := New()
rt.SetMaxCallStackSize(5)
// this test panics as otherwise goja will recover and possibly loop
rt.Set("abort", rt.ToValue(func() {
// panic("waty")
rt.Interrupt("abort this")
}))
var queue = make(chan func() error, 10)
rt.Set("myPromise", func() Value {
p, resolve, _ := rt.NewPromise()
queue <- func() error {
return resolve("some value")
}

return rt.ToValue(p)
})

_, err := rt.RunString(`
let p = myPromise()
p.then(() => { abort() });
`)
if err != nil {
t.Fatal("expected noerror but got error")
}
f := <-queue
err = f()
if err == nil {
t.Fatal("expected error but got no error")
}
t.Log(err)
var soErr *InterruptedError
if !errors.As(err, &soErr) {
t.Fatalf("Wrong error type: %T", err)
}
}

func TestRunLoopPreempt(t *testing.T) {
vm := New()
v, err := vm.RunString("(function() {for (;;) {}})")
Expand Down

0 comments on commit 85fc80e

Please sign in to comment.