V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
GeekHub
KyL
V2EX  ›  JavaScript

JavaScript 事件循环与主代码关系

  •  
  •   KyL · 4 天前 · 762 次点击

    例子:

    <script type="text/javascript"> 
        $.ajax('url', function(data) { 
            console.info('callback'); 
            }); 
    
        //do something 
        console.info('done'); 
    </script> 
    

    如果注释的 do something 占用了很长时间,超过了 ajax 返回的时间,那么显示结果有没有可能是

    callback
    done
    

    ?

    JS 中注册的回调函数,当对应的事件发生时,都会被添加到事件队列中,由事件循环不停轮询。那么<script/>中的"主代码",和事件循环的时间轴关系是怎样的?是"主代码"执行完成后,才会进入事件循环,还是当事件循环中获取一个事件后,就会中断主代码的执行?

    15 条回复    2020-09-19 17:12:56 +08:00
    des
        1
    des   4 天前
    不可能,不会是中断的
    ysc3839
        2
    ysc3839   4 天前 via Android
    个人猜测,只有在当前的 script 代码块执行完后才会去处理在队列中的事件。
    仅仅为猜测,我没有做过任何测试。
    ayase252
        3
    ayase252   4 天前
    有一个任务队列,当请求回来的时候回调函数会被压入到任务队列里。等到当前执行栈为空时,才会到任务队列里取函数执行。
    lisianthus
        4
    lisianthus   4 天前 via Android
    事件循环:先执行宏任务,再执行微任务队列所有任务,再执行宏任务。ajax 我没记错的话是微任务,在宏任务执行后才调用
    nsjs
        5
    nsjs   4 天前
    不会, 你如果在 //do something 那里写个死循环就卡死了,永远也执行不到 done 那里
    ajax 那里只是把你的异步函数放入微队列
    吧?
    crs0910
        6
    crs0910   4 天前   ❤️ 2
    推荐这个
    lin07hui
        7
    lin07hui   4 天前
    const asyncFunc = () =>
    new Promise(resolve => {
    setTimeout(resolve("data"), 500);
    });

    (async () => {
    asyncFunc().then(() => {
    console.log("async callback");
    });
    console.log("async running")

    // do something
    await asyncFunc().then(() => {
    console.log("sync callback");
    });

    console.log("done");
    })();

    // 打印:
    // async running
    // async callback
    // sync callback
    // done
    cheese
        8
    cheese   4 天前   ❤️ 1
    关键词 事件循环 宏任务 微任务。 你说的情况是不会出现的
    ChanKc
        9
    ChanKc   3 天前 via Android
    没有可能
    ChanKc
        10
    ChanKc   3 天前 via Android
    JavaScript 总是 run to finish,当前执行栈是 do something 所在的这个函数,执行完了才会去做 micro task
    ryanlid
        11
    ryanlid   3 天前
    不会

    在事件循环中,一次只处理一个任务,一个任务开始后直到完成,不会被其他任务中断。

    推荐 可以看一下 《 JavaScript 忍者秘籍》第 13 章
    KyL
        12
    KyL   3 天前
    @ChanKc 那么在 Node REPL 或者 Chrome DevTools 中,输入

    $.ajax("url", function() {console.info("callback");});
    就会立刻打印 callback,而不会等到下一句
    console.info("done");
    输入并执行之后才会 callback 。这个是由于脚本文件执行和实时环境的差异造成的吗?
    ChanKc
        13
    ChanKc   3 天前 via Android
    @KyL 你把 done 这句加到上面 ajax 的分号后面试试?
    ChanKc
        14
    ChanKc   3 天前 via Android
    @KyL 我不提倡死记答案,思考几个问题

    1 如果可以出现你主帖提到的情况,是不是意味着需要长时间执行的同步代码可以被异步代码中断?
    2 如果 1 成立,那程序员就不用费劲思考调度了,直接交给 js 引擎智能调度就好?
    3 如果 1 成立,怎么样保证某些代码能够在异步代码之前执行?
    4 如果 1 成立,如何异步代码和同步代码都想要改变某个变量,比方说都给某个变量 foo 赋值,如何解决他们的竞争关系?
    5 思考一下到底$.ajax 时用到了几个线程?回调代码和同步代码都在哪些线程里执行?
    KyL
        15
    KyL   10 小时 21 分钟前
    @ChanKc
    现在我知道了主程序和各种事件都会作为实体项被放到宏任务队列和微任务队列中,且每个实体项都不会被其他事件打断。

    “你把 done 这句加到上面 ajax 的分号后面试试?”,这句话是否暗示着在提示行环境中,每一行代表着任务队列中的一个实体项?

    但是在 Node.js 的 REPL 环境中,如果手动一行一行执行下列代码是可以工作的。

    const { spawn } = require('child_process');

    const child = spawn('cmd', ['ls']);

    child.stdout.on('data', (data) => {
    console.log(`stdout:\n${data}`);
    });

    child.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
    });

    如果 REPL 环境中每一行代码都会作为一个实体项放到任务队列中,那么当

    const child = spawn('cmd', ['ls']);

    执行时,stdout 的 data 事件回调函数还没有绑定,等到数据读完时,没有回调函数可以处理。这其中自相矛盾的地方我百思不得其解。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1110 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 19:34 · PVG 03:34 · LAX 12:34 · JFK 15:34
    ♥ Do have faith in what you're doing.