JS 执行
所有 JavaScript 都在 qzjs 的内部线程上运行。宿主从不直接求值或调用 JS — 公开 API 没有 qz_eval 和 qz_call。代码 通过以下四种方式执行:
initial_script— 运行时启动时求值一次的脚本- 消息驱动 — 宿主投递的 JSON 消息触发 JS 中的处理器
- Web Worker —
new Worker(url)并行运行独立脚本 - 扩展全局对象 — 编译进 qzjs 的 C 扩展向 JS 暴露原生函数
1. 初始脚本
qz_create 在返回前于内部线程上求值 config.initial_script。抛错会使 qz_create 返回 NULL:
c
qz_config_t cfg = {
.initial_script =
"console.log('hello from qzjs');"
"globalThis.onmessage = function (e) { postMessage('got: ' + e.data); };",
.message_cb = on_message,
};
qz_t *rt = qz_create(&cfg); // initial_script 抛错时为 NULL在这里安装消息处理器与顶层状态,然后宿主才开始驱动运行时。
2. 消息驱动执行
宿主通过投递 JSON 消息驱动 JS;JS 用 postMessage 回复:
host ── qz_post_message(json) ──▶ JS: globalThis.onmessage(e)
host ◀── message_cb(json) ─── JS: postMessage(value)qz_post_message线程安全(JSON 被拷贝),可从任意宿主线程调用。- 消息以 JS 对象/字符串经
onmessage到达;e.data是解析后的负载。 message_cb在 qzjs 线程上触发,携带从postMessage序列化的 JSON, 因此回调必须线程安全。
这是宿主 ↔ JS 的唯一数据通道。没有同步返回值 — 结果总是经 message_cb 流回。
3. Web Worker
new Worker(url) 在独立的隔离执行上下文中运行脚本 — 真正的并行工作,而非 共享 JSRuntime 的上下文。Worker 通过 postMessage/onmessage 与其创建者 及彼此通信:
js
// 主脚本
const w = new Worker("worker.js");
w.onmessage = (e) => console.log("from worker:", e.data);
w.postMessage("start");
// worker.js
globalThis.onmessage = (e) => postMessage("echo: " + e.data.cmd);在 -DQZ_PROCESS_MODEL=THREAD 下 worker 运行于并行线程;在默认的 ISOLATED 模型下运行于独立子进程。见 多上下文。
4. 扩展全局对象
原生 C 函数通过把扩展编译进 qzjs(编译期 QZ_EXTENSIONS 表)暴露给 JS, 而非由宿主调用 JS。扩展的 init 钩子在上下文创建时运行,可用引擎的 C API 注册全局对象:
c
#include <qzjs/qzjs.h>
#include <quickjs.h>
#include "qz_internal.h" // qz_get_active_jsctx(内部辅助)
static JSValue js_greet(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) {
const char *name = argc > 0 ? JS_ToCString(ctx, argv[0]) : "world";
JSValue v = JS_NewString(ctx, name);
if (argc > 0) JS_FreeCString(ctx, name);
return v;
}
static int my_ext_init(qz_ext_t *ext, qz_t *rt) {
JSContext *ctx = qz_get_active_jsctx(rt); // 内部辅助
JSValue global = JS_GetGlobalObject(ctx);
JS_SetPropertyStr(ctx, global, "greet",
JS_NewCFunction(ctx, js_greet, "greet", 1));
JS_FreeValue(ctx, global);
return 0;
}在编译期注册扩展(见扩展)。
异步执行
Promise、async/await 与定时器由内部线程上的嵌入式 libuv 循环驱动。 微任务在循环迭代之间自然刷新。setTimeout/ fetch/流会持续推进直到 settle;见事件循环。