Skip to content

Commit 0e14208

Browse files
authored
add support for running the event loop (bellard#14)
* add support for running the event loop allow running of promise/async code * moved loop to interface file+docs+return value * fix * prettier * prettier test file * removed RESERVED_FUNCTIONS
1 parent 8585ecb commit 0e14208

File tree

6 files changed

+112
-41
lines changed

6 files changed

+112
-41
lines changed

c/interface.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,28 @@ char* QTS_GetString(JSContext *ctx, JSValueConst *value) {
238238
return result;
239239
}
240240

241+
/*
242+
runs pending jobs (Promises/async functions) until it encounters
243+
an exception or it executed the passed maxJobsToExecute jobs.
244+
245+
Passing a negative value will run the loop until there are no more
246+
pending jobs or an exception happened
247+
248+
Returns the executed number of jobs or the exception encountered
249+
*/
250+
JSValue *QTS_ExecutePendingJob(JSRuntime *rt, int maxJobsToExecute) {
251+
JSContext *pctx;
252+
int status = 1;
253+
int executed = 0;
254+
for (;executed != maxJobsToExecute && status == 1; executed++) {
255+
status = JS_ExecutePendingJob(rt, &pctx);
256+
}
257+
if (status == -1) {
258+
return jsvalue_to_heap(JS_GetException(pctx));
259+
} else {
260+
return jsvalue_to_heap(JS_NewFloat64(pctx, executed));
261+
}
262+
}
241263

242264
JSValue *QTS_GetProp(JSContext *ctx, JSValueConst *this_val, JSValueConst *prop_name) {
243265
JSAtom prop_atom = JS_ValueToAtom(ctx, *prop_name);

ts/ffi.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ export class QuickJSFFI {
139139
QTS_GetString: (ctx: JSContextPointer, value: JSValuePointer | JSValueConstPointer) => string =
140140
this.module.cwrap("QTS_GetString", "string", ["number","number"])
141141

142+
QTS_ExecutePendingJob: (rt: JSRuntimePointer, maxJobsToExecute: number) => JSValuePointer =
143+
this.module.cwrap("QTS_ExecutePendingJob", "number", ["number","number"])
144+
142145
QTS_GetProp: (ctx: JSContextPointer, this_val: JSValuePointer | JSValueConstPointer, prop_name: JSValuePointer | JSValueConstPointer) => JSValuePointer =
143146
this.module.cwrap("QTS_GetProp", "number", ["number","number","number"])
144147

ts/quickjs-emscripten-module.js

Lines changed: 40 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ts/quickjs.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,25 @@ describe('QuickJSVm', async () => {
258258
})
259259
})
260260

261+
describe('.runEventloop', () => {
262+
it('promises can run', () => {
263+
let i = 0
264+
const fnHandle = vm.newFunction('nextId', () => {
265+
return vm.newNumber(++i)
266+
})
267+
vm.setProp(vm.global, 'nextId', fnHandle)
268+
fnHandle.dispose()
269+
270+
const result = vm.unwrapResult(
271+
vm.evalCode(`(new Promise(resolve => resolve())).then(nextId).then(nextId).then(nextId);1`)
272+
)
273+
assert.equal(i, 0)
274+
vm.runEventloop()
275+
assert.equal(i, 3)
276+
assert.equal(vm.getNumber(result), 1)
277+
})
278+
})
279+
261280
describe('.dump', () => {
262281
function dumpTestExample(val: unknown) {
263282
const json = JSON.stringify(val)

ts/quickjs.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
LowLevelJavascriptVm,
1414
VmPropertyDescriptor,
1515
VmCallResult,
16+
VmEventLoopResult,
1617
VmFunctionImplementation,
1718
} from './vm-interface'
1819
import { QuickJSEmscriptenModule } from './emscripten-types'
@@ -484,6 +485,32 @@ export class QuickJSVm implements LowLevelJavascriptVm<QuickJSHandle> {
484485
return { value: this.heapValueHandle(resultPtr) }
485486
}
486487

488+
/**
489+
* runEventloop(maxJobsToExecute?: number) Runs pendingJobs on the VM
490+
*
491+
* Promises and async functions create pendingJobs these do not execute
492+
* immediately and need to triggered to run.
493+
* You can pass an optional maxJobsToExecute param if you wish to run only
494+
* some of the pendingJobs, by default all pendingJobs are executed until
495+
* either the queue is exausted or the runtime encounters an exception
496+
*
497+
* return value for success is the number of executed jobs
498+
* for error the exception that stopped the execution
499+
*/
500+
runEventloop(maxJobsToExecute?: number): VmEventLoopResult<QuickJSHandle> {
501+
const resultValue = this.heapValueHandle(
502+
this.ffi.QTS_ExecutePendingJob(this.rt.value, maxJobsToExecute || -1)
503+
)
504+
const typeOfRet = this.typeof(resultValue)
505+
if (typeOfRet === 'number') {
506+
const executedJobs = this.getNumber(resultValue)
507+
resultValue.dispose()
508+
return { value: executedJobs }
509+
} else {
510+
return { error: resultValue }
511+
}
512+
}
513+
487514
// customizations
488515

489516
/**

ts/vm-interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type SuccessOrFail<S, F> =
1616
* `{ value: VmHandle } | { error: VmHandle }`.
1717
*/
1818
export type VmCallResult<VmHandle> = SuccessOrFail<VmHandle, VmHandle>
19-
19+
export type VmEventLoopResult<VmHandle> = SuccessOrFail<number, VmHandle>
2020
/**
2121
* A VmFunctionImplementation takes handles as arguments.
2222
* It should return a handle, or be void.

0 commit comments

Comments
 (0)