为何appendChild呈现异步表现?代码行为是否符合预期?
div.appendChild(img)在FileReader的onload外部执行,却要等图片读取完成后才显示? 我在代码中发现一个疑惑点:
div.appendChild(img)并没有放在异步的reader.onload函数内部,这个函数只有在文件数据读取完成后才会执行。按照逻辑,onload事件外部的div.appendChild(img)应该在读取完成前就执行了,此时img还没有有效的src,本来应该不会显示内容。但实际情况是,要等到onload处理器完成、文件读取完毕后图片才显示,我怀疑appendChild是不是异步执行的?请问这个行为是否正确?如果有误请指正。
代码示例
HTML
<input type="file" id="fileInput"> <div id="demoDiv"></div>
JavaScript
let control = document.getElementById('fileInput') let div = document.getElementById('demoDiv') control.addEventListener('change', handleFiles) function handleFiles() { let img = document.createElement('img') let file = this.files[0] let reader = new FileReader(file) reader.readAsDataURL(file) reader.onload = function() { img.src = this.result } div.appendChild(img) }
你的疑惑解答:这个行为是完全正确的,appendChild是同步执行的!
这里的关键不是appendChild的执行时机,而是图片元素的显示逻辑和FileReader的异步特性,拆解一下细节:
appendChild确实是同步执行的
当代码走到div.appendChild(img)这一行时,这个img元素会立刻被添加到DOM树中。但此时img的src属性还没被设置(reader.onload还没触发),所以它只是一个没有内容源的空节点,浏览器不会加载任何资源,自然在页面上看不到它。FileReader的
onload是异步触发的reader.readAsDataURL(file)会启动一个异步的文件读取操作——JavaScript主线程不会等着它完成,而是继续执行后面的appendChild代码。只有当文件读取完成后,浏览器才会回头触发reader.onload回调,这时才会给img设置有效的src(读取到的DataURL)。图片显示的时机是src设置后加载完成时
当img的src被设置为有效的DataURL后,浏览器才会开始解析这个URL并加载图片内容,这个加载过程本身也是异步的。等图片内容加载完成,才会在页面上显示出来。
简单总结整个执行流程:
- 用户选择文件,触发
change事件 - 创建img元素,调用
reader.readAsDataURL(file)启动异步文件读取 - 主线程同步执行
div.appendChild(img),img被加入DOM但无有效src,页面无显示 - FileReader完成文件读取,触发
onload回调,给img设置src为DataURL - 浏览器解析DataURL并加载图片,加载完成后图片显示在页面上
所以你看到的“等读取完毕后图片才显示”,本质是因为img的src是在读取完成后才设置的,和appendChild是否异步没有关系。
内容的提问来源于stack exchange,提问作者appu

