github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_promise.go (about)

     1  package goja
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/nuvolaris/goja/unistring"
     7  )
     8  
     9  type PromiseState int
    10  type PromiseRejectionOperation int
    11  
    12  type promiseReactionType int
    13  
    14  const (
    15  	PromiseStatePending PromiseState = iota
    16  	PromiseStateFulfilled
    17  	PromiseStateRejected
    18  )
    19  
    20  const (
    21  	PromiseRejectionReject PromiseRejectionOperation = iota
    22  	PromiseRejectionHandle
    23  )
    24  
    25  const (
    26  	promiseReactionFulfill promiseReactionType = iota
    27  	promiseReactionReject
    28  )
    29  
    30  type PromiseRejectionTracker func(p *Promise, operation PromiseRejectionOperation)
    31  
    32  type jobCallback struct {
    33  	callback func(FunctionCall) Value
    34  }
    35  
    36  type promiseCapability struct {
    37  	promise               *Object
    38  	resolveObj, rejectObj *Object
    39  }
    40  
    41  type promiseReaction struct {
    42  	capability  *promiseCapability
    43  	typ         promiseReactionType
    44  	handler     *jobCallback
    45  	asyncRunner *asyncRunner
    46  	asyncCtx    interface{}
    47  }
    48  
    49  var typePromise = reflect.TypeOf((*Promise)(nil))
    50  
    51  // Promise is a Go wrapper around ECMAScript Promise. Calling Runtime.ToValue() on it
    52  // returns the underlying Object. Calling Export() on a Promise Object returns a Promise.
    53  //
    54  // Use Runtime.NewPromise() to create one. Calling Runtime.ToValue() on a zero object or nil returns null Value.
    55  //
    56  // WARNING: Instances of Promise are not goroutine-safe. See Runtime.NewPromise() for more details.
    57  type Promise struct {
    58  	baseObject
    59  	state            PromiseState
    60  	result           Value
    61  	fulfillReactions []*promiseReaction
    62  	rejectReactions  []*promiseReaction
    63  	handled          bool
    64  }
    65  
    66  func (p *Promise) State() PromiseState {
    67  	return p.state
    68  }
    69  
    70  func (p *Promise) Result() Value {
    71  	return p.result
    72  }
    73  
    74  func (p *Promise) toValue(r *Runtime) Value {
    75  	if p == nil || p.val == nil {
    76  		return _null
    77  	}
    78  	promise := p.val
    79  	if promise.runtime != r {
    80  		panic(r.NewTypeError("Illegal runtime transition of a Promise"))
    81  	}
    82  	return promise
    83  }
    84  
    85  func (p *Promise) createResolvingFunctions() (resolve, reject *Object) {
    86  	r := p.val.runtime
    87  	alreadyResolved := false
    88  	return p.val.runtime.newNativeFunc(func(call FunctionCall) Value {
    89  			if alreadyResolved {
    90  				return _undefined
    91  			}
    92  			alreadyResolved = true
    93  			resolution := call.Argument(0)
    94  			if resolution.SameAs(p.val) {
    95  				return p.reject(r.NewTypeError("Promise self-resolution"))
    96  			}
    97  			if obj, ok := resolution.(*Object); ok {
    98  				var thenAction Value
    99  				ex := r.vm.try(func() {
   100  					thenAction = obj.self.getStr("then", nil)
   101  				})
   102  				if ex != nil {
   103  					return p.reject(ex.val)
   104  				}
   105  				if call, ok := assertCallable(thenAction); ok {
   106  					job := r.newPromiseResolveThenableJob(p, resolution, &jobCallback{callback: call})
   107  					r.enqueuePromiseJob(job)
   108  					return _undefined
   109  				}
   110  			}
   111  			return p.fulfill(resolution)
   112  		}, nil, "", nil, 1),
   113  		p.val.runtime.newNativeFunc(func(call FunctionCall) Value {
   114  			if alreadyResolved {
   115  				return _undefined
   116  			}
   117  			alreadyResolved = true
   118  			reason := call.Argument(0)
   119  			return p.reject(reason)
   120  		}, nil, "", nil, 1)
   121  }
   122  
   123  func (p *Promise) reject(reason Value) Value {
   124  	reactions := p.rejectReactions
   125  	p.result = reason
   126  	p.fulfillReactions, p.rejectReactions = nil, nil
   127  	p.state = PromiseStateRejected
   128  	r := p.val.runtime
   129  	if !p.handled {
   130  		r.trackPromiseRejection(p, PromiseRejectionReject)
   131  	}
   132  	r.triggerPromiseReactions(reactions, reason)
   133  	return _undefined
   134  }
   135  
   136  func (p *Promise) fulfill(value Value) Value {
   137  	reactions := p.fulfillReactions
   138  	p.result = value
   139  	p.fulfillReactions, p.rejectReactions = nil, nil
   140  	p.state = PromiseStateFulfilled
   141  	p.val.runtime.triggerPromiseReactions(reactions, value)
   142  	return _undefined
   143  }
   144  
   145  func (p *Promise) exportType() reflect.Type {
   146  	return typePromise
   147  }
   148  
   149  func (p *Promise) export(*objectExportCtx) interface{} {
   150  	return p
   151  }
   152  
   153  func (p *Promise) addReactions(fulfillReaction *promiseReaction, rejectReaction *promiseReaction) {
   154  	r := p.val.runtime
   155  	if tracker := r.asyncContextTracker; tracker != nil {
   156  		ctx := tracker.Grab()
   157  		fulfillReaction.asyncCtx = ctx
   158  		rejectReaction.asyncCtx = ctx
   159  	}
   160  	switch p.state {
   161  	case PromiseStatePending:
   162  		p.fulfillReactions = append(p.fulfillReactions, fulfillReaction)
   163  		p.rejectReactions = append(p.rejectReactions, rejectReaction)
   164  	case PromiseStateFulfilled:
   165  		r.enqueuePromiseJob(r.newPromiseReactionJob(fulfillReaction, p.result))
   166  	default:
   167  		reason := p.result
   168  		if !p.handled {
   169  			r.trackPromiseRejection(p, PromiseRejectionHandle)
   170  		}
   171  		r.enqueuePromiseJob(r.newPromiseReactionJob(rejectReaction, reason))
   172  	}
   173  	p.handled = true
   174  }
   175  
   176  func (r *Runtime) newPromiseResolveThenableJob(p *Promise, thenable Value, then *jobCallback) func() {
   177  	return func() {
   178  		resolve, reject := p.createResolvingFunctions()
   179  		ex := r.vm.try(func() {
   180  			r.callJobCallback(then, thenable, resolve, reject)
   181  		})
   182  		if ex != nil {
   183  			if fn, ok := reject.self.assertCallable(); ok {
   184  				fn(FunctionCall{Arguments: []Value{ex.val}})
   185  			}
   186  		}
   187  	}
   188  }
   189  
   190  func (r *Runtime) enqueuePromiseJob(job func()) {
   191  	r.jobQueue = append(r.jobQueue, job)
   192  }
   193  
   194  func (r *Runtime) triggerPromiseReactions(reactions []*promiseReaction, argument Value) {
   195  	for _, reaction := range reactions {
   196  		r.enqueuePromiseJob(r.newPromiseReactionJob(reaction, argument))
   197  	}
   198  }
   199  
   200  func (r *Runtime) newPromiseReactionJob(reaction *promiseReaction, argument Value) func() {
   201  	return func() {
   202  		var handlerResult Value
   203  		fulfill := false
   204  		if reaction.handler == nil {
   205  			handlerResult = argument
   206  			if reaction.typ == promiseReactionFulfill {
   207  				fulfill = true
   208  			}
   209  		} else {
   210  			if tracker := r.asyncContextTracker; tracker != nil {
   211  				tracker.Resumed(reaction.asyncCtx)
   212  			}
   213  			ex := r.vm.try(func() {
   214  				handlerResult = r.callJobCallback(reaction.handler, _undefined, argument)
   215  				fulfill = true
   216  			})
   217  			if ex != nil {
   218  				handlerResult = ex.val
   219  			}
   220  			if tracker := r.asyncContextTracker; tracker != nil {
   221  				tracker.Exited()
   222  			}
   223  		}
   224  		if reaction.capability != nil {
   225  			if fulfill {
   226  				reaction.capability.resolve(handlerResult)
   227  			} else {
   228  				reaction.capability.reject(handlerResult)
   229  			}
   230  		}
   231  	}
   232  }
   233  
   234  func (r *Runtime) newPromise(proto *Object) *Promise {
   235  	o := &Object{runtime: r}
   236  
   237  	po := &Promise{}
   238  	po.class = classObject
   239  	po.val = o
   240  	po.extensible = true
   241  	o.self = po
   242  	po.prototype = proto
   243  	po.init()
   244  	return po
   245  }
   246  
   247  func (r *Runtime) builtin_newPromise(args []Value, newTarget *Object) *Object {
   248  	if newTarget == nil {
   249  		panic(r.needNew("Promise"))
   250  	}
   251  	var arg0 Value
   252  	if len(args) > 0 {
   253  		arg0 = args[0]
   254  	}
   255  	executor := r.toCallable(arg0)
   256  
   257  	proto := r.getPrototypeFromCtor(newTarget, r.global.Promise, r.global.PromisePrototype)
   258  	po := r.newPromise(proto)
   259  
   260  	resolve, reject := po.createResolvingFunctions()
   261  	ex := r.vm.try(func() {
   262  		executor(FunctionCall{Arguments: []Value{resolve, reject}})
   263  	})
   264  	if ex != nil {
   265  		if fn, ok := reject.self.assertCallable(); ok {
   266  			fn(FunctionCall{Arguments: []Value{ex.val}})
   267  		}
   268  	}
   269  	return po.val
   270  }
   271  
   272  func (r *Runtime) promiseProto_then(call FunctionCall) Value {
   273  	thisObj := r.toObject(call.This)
   274  	if p, ok := thisObj.self.(*Promise); ok {
   275  		c := r.speciesConstructorObj(thisObj, r.global.Promise)
   276  		resultCapability := r.newPromiseCapability(c)
   277  		return r.performPromiseThen(p, call.Argument(0), call.Argument(1), resultCapability)
   278  	}
   279  	panic(r.NewTypeError("Method Promise.prototype.then called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   280  }
   281  
   282  func (r *Runtime) newPromiseCapability(c *Object) *promiseCapability {
   283  	pcap := new(promiseCapability)
   284  	if c == r.global.Promise {
   285  		p := r.newPromise(r.global.PromisePrototype)
   286  		pcap.resolveObj, pcap.rejectObj = p.createResolvingFunctions()
   287  		pcap.promise = p.val
   288  	} else {
   289  		var resolve, reject Value
   290  		executor := r.newNativeFunc(func(call FunctionCall) Value {
   291  			if resolve != nil {
   292  				panic(r.NewTypeError("resolve is already set"))
   293  			}
   294  			if reject != nil {
   295  				panic(r.NewTypeError("reject is already set"))
   296  			}
   297  			if arg := call.Argument(0); arg != _undefined {
   298  				resolve = arg
   299  			}
   300  			if arg := call.Argument(1); arg != _undefined {
   301  				reject = arg
   302  			}
   303  			return nil
   304  		}, nil, "", nil, 2)
   305  		pcap.promise = r.toConstructor(c)([]Value{executor}, c)
   306  		pcap.resolveObj = r.toObject(resolve)
   307  		r.toCallable(pcap.resolveObj) // make sure it's callable
   308  		pcap.rejectObj = r.toObject(reject)
   309  		r.toCallable(pcap.rejectObj)
   310  	}
   311  	return pcap
   312  }
   313  
   314  func (r *Runtime) performPromiseThen(p *Promise, onFulfilled, onRejected Value, resultCapability *promiseCapability) Value {
   315  	var onFulfilledJobCallback, onRejectedJobCallback *jobCallback
   316  	if f, ok := assertCallable(onFulfilled); ok {
   317  		onFulfilledJobCallback = &jobCallback{callback: f}
   318  	}
   319  	if f, ok := assertCallable(onRejected); ok {
   320  		onRejectedJobCallback = &jobCallback{callback: f}
   321  	}
   322  	fulfillReaction := &promiseReaction{
   323  		capability: resultCapability,
   324  		typ:        promiseReactionFulfill,
   325  		handler:    onFulfilledJobCallback,
   326  	}
   327  	rejectReaction := &promiseReaction{
   328  		capability: resultCapability,
   329  		typ:        promiseReactionReject,
   330  		handler:    onRejectedJobCallback,
   331  	}
   332  	p.addReactions(fulfillReaction, rejectReaction)
   333  	if resultCapability == nil {
   334  		return _undefined
   335  	}
   336  	return resultCapability.promise
   337  }
   338  
   339  func (r *Runtime) promiseProto_catch(call FunctionCall) Value {
   340  	return r.invoke(call.This, "then", _undefined, call.Argument(0))
   341  }
   342  
   343  func (r *Runtime) promiseResolve(c *Object, x Value) *Object {
   344  	if obj, ok := x.(*Object); ok {
   345  		xConstructor := nilSafe(obj.self.getStr("constructor", nil))
   346  		if xConstructor.SameAs(c) {
   347  			return obj
   348  		}
   349  	}
   350  	pcap := r.newPromiseCapability(c)
   351  	pcap.resolve(x)
   352  	return pcap.promise
   353  }
   354  
   355  func (r *Runtime) promiseProto_finally(call FunctionCall) Value {
   356  	promise := r.toObject(call.This)
   357  	c := r.speciesConstructorObj(promise, r.global.Promise)
   358  	onFinally := call.Argument(0)
   359  	var thenFinally, catchFinally Value
   360  	if onFinallyFn, ok := assertCallable(onFinally); !ok {
   361  		thenFinally, catchFinally = onFinally, onFinally
   362  	} else {
   363  		thenFinally = r.newNativeFunc(func(call FunctionCall) Value {
   364  			value := call.Argument(0)
   365  			result := onFinallyFn(FunctionCall{})
   366  			promise := r.promiseResolve(c, result)
   367  			valueThunk := r.newNativeFunc(func(call FunctionCall) Value {
   368  				return value
   369  			}, nil, "", nil, 0)
   370  			return r.invoke(promise, "then", valueThunk)
   371  		}, nil, "", nil, 1)
   372  
   373  		catchFinally = r.newNativeFunc(func(call FunctionCall) Value {
   374  			reason := call.Argument(0)
   375  			result := onFinallyFn(FunctionCall{})
   376  			promise := r.promiseResolve(c, result)
   377  			thrower := r.newNativeFunc(func(call FunctionCall) Value {
   378  				panic(reason)
   379  			}, nil, "", nil, 0)
   380  			return r.invoke(promise, "then", thrower)
   381  		}, nil, "", nil, 1)
   382  	}
   383  	return r.invoke(promise, "then", thenFinally, catchFinally)
   384  }
   385  
   386  func (pcap *promiseCapability) resolve(result Value) {
   387  	pcap.promise.runtime.toCallable(pcap.resolveObj)(FunctionCall{Arguments: []Value{result}})
   388  }
   389  
   390  func (pcap *promiseCapability) reject(reason Value) {
   391  	pcap.promise.runtime.toCallable(pcap.rejectObj)(FunctionCall{Arguments: []Value{reason}})
   392  }
   393  
   394  func (pcap *promiseCapability) try(f func()) bool {
   395  	ex := pcap.promise.runtime.vm.try(f)
   396  	if ex != nil {
   397  		pcap.reject(ex.val)
   398  		return false
   399  	}
   400  	return true
   401  }
   402  
   403  func (r *Runtime) promise_all(call FunctionCall) Value {
   404  	c := r.toObject(call.This)
   405  	pcap := r.newPromiseCapability(c)
   406  
   407  	pcap.try(func() {
   408  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   409  		iter := r.getIterator(call.Argument(0), nil)
   410  		var values []Value
   411  		remainingElementsCount := 1
   412  		iter.iterate(func(nextValue Value) {
   413  			index := len(values)
   414  			values = append(values, _undefined)
   415  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   416  			alreadyCalled := false
   417  			onFulfilled := r.newNativeFunc(func(call FunctionCall) Value {
   418  				if alreadyCalled {
   419  					return _undefined
   420  				}
   421  				alreadyCalled = true
   422  				values[index] = call.Argument(0)
   423  				remainingElementsCount--
   424  				if remainingElementsCount == 0 {
   425  					pcap.resolve(r.newArrayValues(values))
   426  				}
   427  				return _undefined
   428  			}, nil, "", nil, 1)
   429  			remainingElementsCount++
   430  			r.invoke(nextPromise, "then", onFulfilled, pcap.rejectObj)
   431  		})
   432  		remainingElementsCount--
   433  		if remainingElementsCount == 0 {
   434  			pcap.resolve(r.newArrayValues(values))
   435  		}
   436  	})
   437  	return pcap.promise
   438  }
   439  
   440  func (r *Runtime) promise_allSettled(call FunctionCall) Value {
   441  	c := r.toObject(call.This)
   442  	pcap := r.newPromiseCapability(c)
   443  
   444  	pcap.try(func() {
   445  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   446  		iter := r.getIterator(call.Argument(0), nil)
   447  		var values []Value
   448  		remainingElementsCount := 1
   449  		iter.iterate(func(nextValue Value) {
   450  			index := len(values)
   451  			values = append(values, _undefined)
   452  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   453  			alreadyCalled := false
   454  			reaction := func(status Value, valueKey unistring.String) *Object {
   455  				return r.newNativeFunc(func(call FunctionCall) Value {
   456  					if alreadyCalled {
   457  						return _undefined
   458  					}
   459  					alreadyCalled = true
   460  					obj := r.NewObject()
   461  					obj.self._putProp("status", status, true, true, true)
   462  					obj.self._putProp(valueKey, call.Argument(0), true, true, true)
   463  					values[index] = obj
   464  					remainingElementsCount--
   465  					if remainingElementsCount == 0 {
   466  						pcap.resolve(r.newArrayValues(values))
   467  					}
   468  					return _undefined
   469  				}, nil, "", nil, 1)
   470  			}
   471  			onFulfilled := reaction(asciiString("fulfilled"), "value")
   472  			onRejected := reaction(asciiString("rejected"), "reason")
   473  			remainingElementsCount++
   474  			r.invoke(nextPromise, "then", onFulfilled, onRejected)
   475  		})
   476  		remainingElementsCount--
   477  		if remainingElementsCount == 0 {
   478  			pcap.resolve(r.newArrayValues(values))
   479  		}
   480  	})
   481  	return pcap.promise
   482  }
   483  
   484  func (r *Runtime) promise_any(call FunctionCall) Value {
   485  	c := r.toObject(call.This)
   486  	pcap := r.newPromiseCapability(c)
   487  
   488  	pcap.try(func() {
   489  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   490  		iter := r.getIterator(call.Argument(0), nil)
   491  		var errors []Value
   492  		remainingElementsCount := 1
   493  		iter.iterate(func(nextValue Value) {
   494  			index := len(errors)
   495  			errors = append(errors, _undefined)
   496  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   497  			alreadyCalled := false
   498  			onRejected := r.newNativeFunc(func(call FunctionCall) Value {
   499  				if alreadyCalled {
   500  					return _undefined
   501  				}
   502  				alreadyCalled = true
   503  				errors[index] = call.Argument(0)
   504  				remainingElementsCount--
   505  				if remainingElementsCount == 0 {
   506  					_error := r.builtin_new(r.global.AggregateError, nil)
   507  					_error.self._putProp("errors", r.newArrayValues(errors), true, false, true)
   508  					pcap.reject(_error)
   509  				}
   510  				return _undefined
   511  			}, nil, "", nil, 1)
   512  
   513  			remainingElementsCount++
   514  			r.invoke(nextPromise, "then", pcap.resolveObj, onRejected)
   515  		})
   516  		remainingElementsCount--
   517  		if remainingElementsCount == 0 {
   518  			_error := r.builtin_new(r.global.AggregateError, nil)
   519  			_error.self._putProp("errors", r.newArrayValues(errors), true, false, true)
   520  			pcap.reject(_error)
   521  		}
   522  	})
   523  	return pcap.promise
   524  }
   525  
   526  func (r *Runtime) promise_race(call FunctionCall) Value {
   527  	c := r.toObject(call.This)
   528  	pcap := r.newPromiseCapability(c)
   529  
   530  	pcap.try(func() {
   531  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   532  		iter := r.getIterator(call.Argument(0), nil)
   533  		iter.iterate(func(nextValue Value) {
   534  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   535  			r.invoke(nextPromise, "then", pcap.resolveObj, pcap.rejectObj)
   536  		})
   537  	})
   538  	return pcap.promise
   539  }
   540  
   541  func (r *Runtime) promise_reject(call FunctionCall) Value {
   542  	pcap := r.newPromiseCapability(r.toObject(call.This))
   543  	pcap.reject(call.Argument(0))
   544  	return pcap.promise
   545  }
   546  
   547  func (r *Runtime) promise_resolve(call FunctionCall) Value {
   548  	return r.promiseResolve(r.toObject(call.This), call.Argument(0))
   549  }
   550  
   551  func (r *Runtime) createPromiseProto(val *Object) objectImpl {
   552  	o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
   553  	o._putProp("constructor", r.global.Promise, true, false, true)
   554  
   555  	o._putProp("catch", r.newNativeFunc(r.promiseProto_catch, nil, "catch", nil, 1), true, false, true)
   556  	o._putProp("finally", r.newNativeFunc(r.promiseProto_finally, nil, "finally", nil, 1), true, false, true)
   557  	o._putProp("then", r.newNativeFunc(r.promiseProto_then, nil, "then", nil, 2), true, false, true)
   558  
   559  	o._putSym(SymToStringTag, valueProp(asciiString(classPromise), false, false, true))
   560  
   561  	return o
   562  }
   563  
   564  func (r *Runtime) createPromise(val *Object) objectImpl {
   565  	o := r.newNativeConstructOnly(val, r.builtin_newPromise, r.global.PromisePrototype, "Promise", 1)
   566  
   567  	o._putProp("all", r.newNativeFunc(r.promise_all, nil, "all", nil, 1), true, false, true)
   568  	o._putProp("allSettled", r.newNativeFunc(r.promise_allSettled, nil, "allSettled", nil, 1), true, false, true)
   569  	o._putProp("any", r.newNativeFunc(r.promise_any, nil, "any", nil, 1), true, false, true)
   570  	o._putProp("race", r.newNativeFunc(r.promise_race, nil, "race", nil, 1), true, false, true)
   571  	o._putProp("reject", r.newNativeFunc(r.promise_reject, nil, "reject", nil, 1), true, false, true)
   572  	o._putProp("resolve", r.newNativeFunc(r.promise_resolve, nil, "resolve", nil, 1), true, false, true)
   573  
   574  	r.putSpeciesReturnThis(o)
   575  
   576  	return o
   577  }
   578  
   579  func (r *Runtime) initPromise() {
   580  	r.global.PromisePrototype = r.newLazyObject(r.createPromiseProto)
   581  	r.global.Promise = r.newLazyObject(r.createPromise)
   582  
   583  	r.addToGlobal("Promise", r.global.Promise)
   584  }
   585  
   586  func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) {
   587  	f, _ := AssertFunction(fObj)
   588  	return func(x interface{}) {
   589  		_, _ = f(nil, r.ToValue(x))
   590  	}
   591  }
   592  
   593  // NewPromise creates and returns a Promise and resolving functions for it.
   594  //
   595  // WARNING: The returned values are not goroutine-safe and must not be called in parallel with VM running.
   596  // In order to make use of this method you need an event loop such as the one in goja_nodejs (https://github.com/nuvolaris/goja_nodejs)
   597  // where it can be used like this:
   598  //
   599  //	loop := NewEventLoop()
   600  //	loop.Start()
   601  //	defer loop.Stop()
   602  //	loop.RunOnLoop(func(vm *goja.Runtime) {
   603  //	    p, resolve, _ := vm.NewPromise()
   604  //	    vm.Set("p", p)
   605  //	    go func() {
   606  //	        time.Sleep(500 * time.Millisecond)   // or perform any other blocking operation
   607  //	        loop.RunOnLoop(func(*goja.Runtime) { // resolve() must be called on the loop, cannot call it here
   608  //	            resolve(result)
   609  //	        })
   610  //	    }()
   611  //	}
   612  func (r *Runtime) NewPromise() (promise *Promise, resolve func(result interface{}), reject func(reason interface{})) {
   613  	p := r.newPromise(r.global.PromisePrototype)
   614  	resolveF, rejectF := p.createResolvingFunctions()
   615  	return p, r.wrapPromiseReaction(resolveF), r.wrapPromiseReaction(rejectF)
   616  }
   617  
   618  // SetPromiseRejectionTracker registers a function that will be called in two scenarios: when a promise is rejected
   619  // without any handlers (with operation argument set to PromiseRejectionReject), and when a handler is added to a
   620  // rejected promise for the first time (with operation argument set to PromiseRejectionHandle).
   621  //
   622  // Setting a tracker replaces any existing one. Setting it to nil disables the functionality.
   623  //
   624  // See https://tc39.es/ecma262/#sec-host-promise-rejection-tracker for more details.
   625  func (r *Runtime) SetPromiseRejectionTracker(tracker PromiseRejectionTracker) {
   626  	r.promiseRejectionTracker = tracker
   627  }
   628  
   629  // SetAsyncContextTracker registers a handler that allows to track async execution contexts. See AsyncContextTracker
   630  // documentation for more details. Setting it to nil disables the functionality.
   631  // This method (as Runtime in general) is not goroutine-safe.
   632  func (r *Runtime) SetAsyncContextTracker(tracker AsyncContextTracker) {
   633  	r.asyncContextTracker = tracker
   634  }