其实在Promise之外也存在这个问题,这里我们以一般的使用情况来考虑此问题。
这个问题的本质是接收回调函数的函数,会根据具体的执行情况,可以选择是以同步还是异步的方式对回调函数进行调用。
下面我们以 onReady(fn) 为例进行说明,这个函数会接收一个回调函数进行处理。
mixed-onready.js
function onReady(fn) { var readyState = document.readyState; if (readyState == ‘interactive‘ || readyState === ‘complete‘) { fn(); } else { window.addEventListener(‘DOMContentLoaded‘, fn); } } onReady(function () { console.log(‘DOM fully loaded and parsed‘); }); console.log(‘==Starting==‘);
mixed-onready.js会根据执行时的DOM是否装载完毕来决定是对回调函数进行同步或者是异步的调用。
如果在调用onReady之前DOM已经载入的话
对回调函数进行同步调用
如果在调用onReady之前DOM还没有载入的话
通过注册 DOMContentLoaded 事件监听器来对回调函数进行异步调用
因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。
为了解决这个问题,我们可以选择统一使用异步调用方法。
async-onready.js
function onReady(fn) { var readyState = document.readyState; if (readyState == ‘interactive‘ || readyState === ‘complete‘) { setTimeout(fn, 0); } else { window.addEventListener(‘DOMContentLoaded‘, fn); } } onReady(function () { console.log(‘DOM fully loaded and parsed‘); }); console.log(‘==Starting==‘);
关于这个问题,在 Effective JavaScript 23 的 第67项 不要对异步回调函数进行同步调用中也有详细介绍。
• 绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用。
• 如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果。
• 对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题。
• 如果想在将来某时刻调用异步回调函数的话,可以使用 setTimeout等异步API。
— David Herman Effective JavaScript
时间: 2024-10-09 21:16:49