...

Source file src/github.com/dop251/goja/builtin_promise.go

Documentation: github.com/dop251/goja

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

View as plain text