乐趣区

关于前端:关于JS-consolelog-是同步-or-异步引发的问题

发现问题

在学习《Vue.js 设计与实现》渲染器局部的时候,发现在调用渲染函数之后批改虚构 DOM 的值,渲染函数中拿到的是新的虚构 DOM。

const vnode = {
    type: "div",
    children: [
        {
            type: "p",
            children: "text",
        },
    ],
};
const app = document.querySelector("#app");
renderer.render(vnode, app);
vnode.children = [
    {
        type: "input",
        props: {value: "请输出关键字",},
    },
];
  • 在渲染函数中打印虚构 DOM 的值,发现后果为

把批改虚构 DOM 的代码放入 setTimeout 函数中

setTimeout(() => {
    vnode.children = [
        {
            type: "input",
            props: {value: "请输出关键字",},
        },
    ];
});

发现打印后果没有扭转依然是批改后的虚构 DOM。
再将 setTimeout 的回调工夫批改为 2000 ms,才失去了我想要的后果。

由此揣测,渲染函数中存在有导致异步操作的代码。

定位问题

依据渲染流程,在代码中找到了可能导致异步的代码:

  1. document.createElement(localName, options);
  2. insertBefore(node, child)

接着去 Dom Standard 中查找这两个办法的具体定义:
在 createElement 的第六步中返回了 creating an element 的后果

于是去查看 creating an element 的定义:

然而在定义中并没有找到和异步操作无关的信息。
接着去查看 insertBefore 的具体定义,后果也是一样,没有证据阐明 insertBefore 波及到了异步操作。
那么同步的操作却导致了异步的后果,那么问题很可能不是出在渲染函数中,于是我间接应用 console.log() 打印虚构 DOM,果然,失去的是最新的虚构 DOM:

console.log(vnode);
setTimeout(() => {
    vnode.children = [
        {
            type: "input",
            props: {value: "请输出关键字",},
        },
    ];
});

查阅了一些帖子后发现有人提到在《你不晓得的 JavaScript 中卷》中有问题的答案:

异步控制台

并没有什么标准或一组需要指定 console.* 办法族如何工作——它们并不是 JavaScript 正式的一部分,而是由宿主环境 (请参考本书的“类型和语法”局部) 增加到 JavaScript 中的。因而,不同的浏览器和 JavaScript 环境能够依照本人的志愿来实现,有时候这会引起混同。
尤其要提出的是,在某些条件下,某些浏览器的 console.log(..) 并不会把传入的内容立刻输入。呈现这种状况的次要起因是,在许多程序 (不只是 JavaScript) 中,I/O 是十分低速的阻塞局部。所以,(从页面 /UI 的角度来说)浏览器在后盾异步解决控制台 I/O 可能进步性能,这时用户甚至可能基本意识不到其产生。

举例

同时书中给出了解决方案:

如果遇到这种少见的状况,最好的抉择是在 JavaScript 调试器中应用断点,而不要依赖控制台输入。次优的计划是把对象序列化到一个字符串中,以强制执行一次“快照”,比方通过 JSON.stringify(..)。

问题解决:

let vnodeStr = JSON.parse(JSON.stringify(vnode));
console.log(vnodeStr);

参考资料

  1. Dom Standard
  2. console.log 是异步流?感觉本人貌似踩了个坑
  3. 《你不晓得的 JavaScript 中卷》
退出移动版