zoukankan      html  css  js  c++  java
  • 关于我对js运行时的一些浅薄的理解:(2,给我们的js异步的能力,eventloop)

    quickjs 究竟是什么

    上一篇讲到这个轮子本质上其实就是一个编译器了。或者说是一个独立的js引擎。通过混入原生环境的方式大幅提高性能!当然了,也可以看作运行时,因为已经是一个可以进行系统调用的平台了
    QuickJS 出自传奇程序员 Fabrice Bellard(膜拜)

    给我们的js实现简单的异步

    异步并非天生就有,计算机执行代码只有同步。所谓的异步无非就是用同步去模拟异步。一个很经典的思路是轮询(poll)
    poll这个词在操作系统很常见,本质上就是通过等待队列实现异步。

    前置知识 轮询和中断(不懂的建议复习操作系统)

    quickjs中settimeout实现

    虽然我们几乎每天都用到settimeout这api,但js引擎并没有这个api,这个api是每个运行时自己实现的,q浏览器和nodejs本身都实现了自己的setimeout

    import { setTimeout } from "os";
    
    setTimeout(() => { /* ... */ }, 0);
    

    这就是在quickjs中调用settimeout的方式,和之前的fib并没啥区别

    static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
                                   int argc, JSValueConst *argv)
    {
       int64_t delay;
       JSValueConst func;
       JSOSTimer *th;
       JSValue obj;
    
       func = argv[0];
       if (!JS_IsFunction(ctx, func))
           return JS_ThrowTypeError(ctx, "not a function");
       if (JS_ToInt64(ctx, &delay, argv[1]))
           return JS_EXCEPTION;
       obj = JS_NewObjectClass(ctx, js_os_timer_class_id);
       if (JS_IsException(obj))
           return obj;
       th = js_mallocz(ctx, sizeof(*th));
       if (!th) {
           JS_FreeValue(ctx, obj);
           return JS_EXCEPTION;
       }
       th->has_object = TRUE;
       th->timeout = get_time_ms() + delay;
       th->func = JS_DupValue(ctx, func);
       list_add_tail(&th->link, &os_timers);
       JS_SetOpaque(obj, th);
       return obj;
    }
    

    源码很简单,就是可以看出,这个 setTimeout 的实现中,并没有任何多线程或 poll 的操作,只是把一个存储 timer 信息的结构体通过 JS_SetOpaque 的方式,挂到了最后返回的 JS 对象上而已,是个非常简单的同步操作。因此,就和调用原生 fib 函数一样地,在 eval 执行 JS 代码时,遇到 setTimeout 后也是同步地执行一点 C 代码后就立刻返回,没有什么特别之处。
    为什么能异步,可以翻看我之前的那个文章实现fib时候的代码,很简单,main函数里调用了loop()

    int main(int argc, char **argv)
    {
      // ...
      // eval JS 字节码
      js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0);
      // 启动 Event Loop
      js_std_loop(ctx);
      // ...
    }
    

    而loop实现也很简单

    /* main loop which calls the user JS callbacks */
    void js_std_loop(JSContext *ctx)
    {
        JSContext *ctx1;
        int err;
    
        for(;;) {
            /*在这里执行pending操作*/
            for(;;) {
                err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
                if (err <= 0) {
                    if (err < 0) {
                        js_std_dump_error(ctx1);
                    }
                    break;
                }
            }
    
            if (!os_poll_func || os_poll_func(ctx))
                break;
        }
    }
    

    和网络上所有的evetloop博客写的一样,双重无限循环,先执行所有的回调,然后在进行os_poll_func
    这里的os_poll_func
    就是原理类似的 poll 系统调用,从而可以借助操作系统的能力,使得只在【定时器触发、文件描述符读写】等事件发生时,让进程回到前台执行一个 tick,把此时应该运行的 JS 回调跑一遍,而其余时间都在后台挂起。在这条路上继续走下去,就能以经典的异步非阻塞方式来实现整个运行时啦。

  • 相关阅读:
    ZOJ 3765 Lights (zju March I)伸展树Splay
    UVA 11922 伸展树Splay 第一题
    UVALive 4794 Sharing Chocolate DP
    ZOJ 3757 Alice and Bod 模拟
    UVALive 3983 捡垃圾的机器人 DP
    UVA 10891 SUM游戏 DP
    poj 1328 Radar Installatio【贪心】
    poj 3264 Balanced Lineup【RMQ-ST查询区间最大最小值之差 +模板应用】
    【转】RMQ-ST算法详解
    poj 3083 Children of the Candy Corn 【条件约束dfs搜索 + bfs搜索】【复习搜索题目一定要看这道题目】
  • 原文地址:https://www.cnblogs.com/geck/p/13664370.html
Copyright © 2011-2022 走看看