大多数JavaScript / HTML / DOM /浏览器回调和事件不是立即发生的,也不是不可重入的(不确定此处是否正确)。我的意思是,即使有事件发生,它也被添加到浏览器的队列中,直到当前事件退出后才进行处理。
例如,如果我尝试加载图像
const img = new Image();
img.onload = function() {
console.log("image loaded");
}
img.src = 'https://i.imgur.com/ZKMnXce.png';
for (let i = 0; i < 100000; ++i) {
console.log(i);
}
假定浏览器没有超时,则可以保证在传递for
事件之前运行以上onload
循环。
另一个示例可能是postMessage
window.onmessage = function() {
console.log('got message');
}
window.postMessage({}, '*');
for (let i = 0; i < 100000; ++i) {
console.log(i);
}
同样,循环将在消息事件到达之前结束。
这2个都是事件,但是当然有像setTimeout
和requestAnimationFrame
这样的函数需要回调。
requestAnimationFrame(function() {
console.log('requestAnimationFrame');
});
setTimeout(function() {
console.log('setTimeout');
}, 0);
for (let i = 0; i < 100000; ++i) {
console.log(i);
}
同样,循环将在调用rAF或setTimeout回调之前完成。
现在当然可以手动编写采用立即调用的回调的API。实际上,AFAICT是一个常见错误,被认为是一种反模式。例子
/*
* @param {*} params The params
* @param {function(error, result)} callback where first param is error, null on success
*/
function doSomethingAsyncThenCallback(params, callback) {
if (params are bad) {
callback("bad params");
return;
}
dSomethingAsync then callback(null, result)
}
被认为是错误模式的AFAIK,因为用户可能不希望回调函数在当前事件退出之前到达。推荐的写类似方法的方法是
/*
* @param {*} params The params
* @param {function(error, result)} callback where first param is error
*/
function someSomethingAsyncThenCallback(params, callback) {
if (params are bad) {
setTimeout(() => { callback("bad params"); }, 0);
return;
}
do something async then callback(null, result)
}
这样,无论用户使用什么代码,回调都将在当前事件结束后到达。
所以,我的问题是:是否有可能会出现回调/事件的浏览器API(如内置在浏览器中的API,例如XHR,canvas 2d,WebGL,WebAudio,加密,游戏手柄,websocket,webrtc,localstorage等)当前事件即将结束之前吗?
我知道JavaScript不能被打断。但是,就像上面执行不佳的JS api一样,成功后将等待当前事件退出,但失败后将立即调用回调,它们的本机浏览器API是否都具有这种行为?