繁体   English   中英

JavaScript fetch() 分辨率的行为不清楚

[英]Unclear behavior of JavaScript fetch() resolution

我试图理解 fetch() 的执行流程,但这与我的实验不一致。

我找到了这个答案https://stackoverflow.com/a/62566665/15933760

fetch 方法确实会立即启动一个网络请求,该请求将被“并行”处理,即在事件循环之外。 当接收到请求响应(不是它的主体,通常是标头)时,浏览器将排队一个新任务,该任务仅负责解析 Promise,从而将其微任务排队。

实验

我理解它,除了任务/微任务排队位。 这是我尝试进行的实验:

function delay() {
        i=0;
        while(i<1000000000) i++;
}

console.log('start');

setTimeout(()=> {
        console.log('start macrotask');
        delay();
        console.log('end macrotask');
}, 0);

Promise.resolve().then(() => {
        console.log('start promise handler');
        delay();
        console.log('end promise handler');
});

console.log('calling fetch');
fetch('http://127.0.0.1:9999/').then(function(result) {
        console.log('success start fetch handler');
        delay();
        console.log('success end fetch handler');
},
function(error) {
        console.log('error start fetch handler');
        delay();
        console.log('error end fetch handler');
});
console.log('done calling fetch');

delay();
delay();
delay();
delay();
delay();
delay();
delay();
delay();
delay();
delay();
delay();
delay();
delay();
Promise.resolve().then(() => {
        console.log('start 2nd promise handler');
        delay();
        console.log('end 2nd promise handler');
});
setTimeout(()=> {
        console.log('start 2nd macrotask');
        delay();
        console.log('end 2nd macrotask');
}, 0);
delay();

console.log('end');

在我的 Chrome 控制台中,这会输出:

start
calling fetch
done calling fetch
end
start promise handler
end promise handler
start 2nd promise handler
end 2nd promise handler
undefined
success start fetch handler
success end fetch handler
start macrotask
end macrotask
start 2nd macrotask
end 2nd macrotask

查看我的本地流量,当它输出calling fetch时,会向 127.0.0.1:9999 发出连接和请求。 此外,如果我将套接字编程为不立即响应,则执行将继续,并且我会立即看到done calling fetch

到目前为止,这一切都是一致的,并表明 fetch 与主线程并行执行。

问题

  1. 第一个主线程正在执行,所以我们看到start
  2. setTimeout 回调作为宏任务排队
  3. 第一个 Promise 的处理程序作为微任务排队
  4. 我们看到calling fetch done calling fetch因为 fetch() 继续并行执行,即使在那之后
  5. 除了我查看流量以见证连接形成、请求和回复被发送以及套接字连接干净地关闭之外,主线程等待大量时间与delay()调用
  6. fetch() 并行完成它的工作,我希望它要么
    • 选项 1满足 promise 并因此将其处理程序作为微任务排队,或
    • 选项 2排队一个任务(宏任务),然后将完成 promise。

选项1

  1. promise 处理程序作为微任务排队
  2. 在代码的 rest 之后,它查询另一个微任务(最后一个承诺)
  3. 接着排队一个宏任务(最后一次 setTimeout 调用)

这让我们的队列看起来像这样:

microtask queue:  first promise handler
                  fetch handler
                  last promise handler

macrotask queue:  first timeout callback
                  last timeout callback

这不一致,因为我们从 output 看到的顺序是:

first promise handler
last promise handler
fetch handler
first timeout callback
last timeout callback

选项 2

  1. 完成获取 promise 的宏任务进入队列
  2. 在代码的 rest 之后,它查询一个微任务(最后一个承诺)
  3. 接着排队一个宏任务(最后一次 setTimeout 调用)

这让我们的队列看起来像这样:

microtask queue:  first promise handler
                  last promise handler

macrotask queue:  first timeout callback
                  fetch task
                  last timeout callback

这再次不一致,因为我们从 output 看到的顺序是:

first promise handler
last promise handler
fetch handler
first timeout callback
last timeout callback

为了保持一致,它应该在第一个超时回调任务之后执行 promise-resolving 任务。

你说得对, Promises 创建微任务并setTimeoutfetch创建任务(宏任务); 我认为这里的问题是延迟0setTimeout不一定会立即排队或与fetch在同一个任务队列中。

  • MDN 的setTimeout页面列出了延迟超时的几个原因,包括“如果页面(或操作系统/浏览器)忙于其他任务”。
  • WHATWG 的 HTML “在定时器之后运行步骤”规范(从setTimeout定义第 8.6 节链接)允许实现定义的额外延迟(第 5.3 点),该延迟可以降低定时器分辨率以节省功耗和其他原因。
  • WHATWG 关于事件循环的 HTML 规范允许实现者选择首先拉取哪个任务队列。 setTimeout使用“计时器任务源”,而fetch使用“网络任务源”,因此这些队列中的顺序得到保证,但它们之间的顺序不是:

    让 taskQueue 成为一个这样的任务队列,以实现定义的方式选择。

  • 正如Jake Archibald 在 2015 年记录的那样,即使浏览器实现规范的方式也略有不同。

    因此,Chrome 做到了这一点。 对我来说“新闻”的一点是微任务在回调之后处理(只要没有其他 JavaScript 正在执行中),我认为它仅限于任务结束。 此规则来自用于调用回调的 HTML 规范:

    [引用]

    …并且微任务检查点涉及通过微任务队列,除非我们已经在处理微任务队列。

只要延迟为 0 毫秒的setTimeout可能不会立即将任务排入与fetch相同的任务队列,那么行为是自洽的,即使在实践中它可能因浏览器而异

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM