js中函数和变量的访问顺序?

新学HTML5,遇到个问题,我在利用input type="file"控件读取本地的JSON文件里的JSON数据并插入到body中去,代码如下:

<script>
    var aa = 1;
    var loadFiles = document.getElementById("loadFiles");//获取id为loadFiles的input对象
    function handleFiles(files){
        if(files.length){
            var file = files[0];
            var reader = new FileReader();
            reader.readAsText(file);
            reader.onload = function(){
                aa = 2;
                console.log(aa);
            };
         
            console.log(aa);
        }
    }
</script>
<body>
<input type="file" id='loadFiles' onchange="handleFiles(this.files)" />
<div id="filesContent" ></div>
</body>

我发现两次console.log打印的结果,反而是先aa=1被打印出来,其次onload函数里面的aa=2才被打印出来,我查资料得知reader的onload方法是在它调用readAsText方法后立即调用的,为什么会先打印后面的console(也就是说我在onload方法外面调用的全是aa=1),这个onload函数和后面的console调用顺序究竟是怎样?

阅读 4.7k
4 个回答

跟文件大小、读取时间耗时长短无关。
先输出1后输出2是必然的!

证明如下:

首先写一个阻塞函数,用于阻塞JS执行线程一段时间:

function block(delay) {
    var end = +new Date() + delay;
    while(+new Date() <= end){}
}

然后,把它加到你的代码中:

reader.readAsText();
block(5000);
reader.onload = function() {
    console.log(2);
};
block(5000);
console.log(1);

结果是什么?是不论你阻塞多长时间,结果都是先输出1,后输出2。

结论就是:

onload回调函数被设计成了不在当前这一轮事件循环调用

所以,哪怕文件内容瞬间就被读完了,结果也是先输出1。
这不是一个概率性的结论,而是一个确定的结论。

参考:彻底理解同步、异步和事件循环

onload是一个事件,当读取操作完成时触发并调用,这相当于一个异步回调,而读取文件消耗的时间绝大多数情况下都要慢于程序的执行,所以自然是先执行log a=1的情况。

readAsText 再快也是有时滞的,有相当大的几率会落在顺序执行的 console.log(1) 之后执行

因为js是单线程执行的,会把当前代码流先执行完。就算onload事件触发了,响应事件也只是会放入到队列中,等待当前代码先执行完再执行。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏