builtin_promise.go 19 KB

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