我在JS中测试了异步代码的概念。在回调队列和放大器之间感到困惑微任务队列顺序。每当promise对象得到解析时,执行方法{then}被推入微任务队列,同时浏览器计时器函数(如setTimeout)的回调被推送到回调队列中。每当调用堆栈变空时,事件循环不断检查队列并将函数从队列推送到调用堆栈。事件循环应该优先于正常回调队列上的微任务队列,但在示例中:https://jsfiddle.net/BHUPENDRA1011/2n89ftmp/否则就会发生这种情况。
function display(data) {
console.log('hi back from fetch');
}
function printHello() {
console.log('hello');
}
function blockfor300ms() {
for (let i = 0; i < 300; i++) {
// just delaying logic
}
}
// this sets the printHello function in callback queue { event loop queue }
setTimeout(printHello, 0);
const futureData = fetch('https://api.github.com/users/xiaotian/repos');
// after promise is resolved display function gets added into other queue : Microtask queue { job queue}
futureData.then(display);
// event loop gives prefrence to Microtask queue ( untill its complete)
blockfor300ms();
// which runs first
console.log('Me first !')
预期输出
实际输出:
请告诉我这是怎么回事。
由于
答案 0 :(得分:2)
恭喜,您一定要参加Will Sentence的前端大师课程“ JavaScript:新的硬部分”! :)
很棒的课程,以及测试此示例的荣誉。
现在为您解释:
虽然是事实,但“ kib”表示什么:
“您的函数blockfor300ms不会将线程阻塞足够长的时间 提取以接收响应”
不幸的是,这无关紧要,因为即使您直到收到对fetch调用的响应之后才阻止执行,您仍然会看到相同的结果... (请参见下面的示例代码片段,您可以使用警报框或长时间循环的非异步XMLHttpRequest调用阻止执行,我收到的结果相同)
不幸的是,抓取无法按照我发现的所有博客和资源的描述进行操作...说明
Fetch会将其诺言链添加到微任务队列中,并在事件循环的下一个刻度之前的任何回调之前运行。
在下面的示例代码中,看来获取并不会像Will和其他人所描述的那样简单地将其解析处理程序功能添加到微任务队列,因为正如Lewis所说,因为它需要网络活动,因此正在由网络任务源处理。但是,我不认为这是由于printHello“阻止”了网络任务,因为在下面的示例代码中,提取操作是在printHello之前触发的,并且网络响应也将在计时器完成之前发生。
如您在下面的示例中看到的那样,在收到获取响应(2000毫秒)后很长时间,我就延迟了将printHello添加到任务队列的时间,但是如果我们将代码执行时间超过2000毫秒(因此仍在运行执行上下文),则将首先打印“ Hello”。这意味着fetch resolve()处理程序不会被简单地添加到与其他promise处理程序一起触发的微任务队列中。
那么,如果在计时器任务完成之前(2000毫秒)接收到响应并理论上将其添加到任务队列中,为什么仍在回调后记录它?那么,我的猜测是计时器任务源必须比网络任务源具有更高的优先级。因此,两者都坐在他们的任务队列中,但是计时器任务队列在网络任务队列之前触发...
链接到规范:
计时器任务源-https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timer-task-source 联网任务源-https://html.spec.whatwg.org/multipage/webappapis.html#networking-task-source
function display(data){console.log("Fetch resolved!")}
function printHello(){console.log("Callback Time")}
function blockExecution() {
console.log("Blocking execution...");
alert('Wait at least 2000ms before closing');
}
const futureData = fetch('https://jsonplaceholder.typicode.com/todos/1');
futureData.then(response => response.json()).then(display);
setTimeout(printHello, 2000);
const p = new Promise(
// this is called the "executor"
(resolve, reject) => {
console.log("I'm making a promise...");
resolve("Promise resolved!");
console.log("Promise is made...");
}
);
p.then(
// this is called the success handler
result => console.log(result)
);
blockExecution();
console.log("Execution ended!");
答案 1 :(得分:0)
futureData
实际上是一个获取承诺,因此在调用fetch
时,绝对有一个排队到任务队列的网络任务。结果,printHello
肯定会在网络任务之前执行,因为它们都是两个任务。并且只有在解决网络任务的承诺时,方法display
才会被置于微任务队列中。根据定义,Microtasks仅在每项任务结束时执行。因此,display
将在网络任务结束时调用,printHello
已经很久才被调用。
如果您希望在display
之前调用printHello
,futureData
必须只排队微任务。让我们稍微修改你的例子。
function display(data) {
console.log('hi back from fetch');
}
function printHello() {
console.log('hello');
}
let now = Date.now();
function executeFutureDataWithMicrotasksOnly() {
// Execute microtasks continually in 300ms.
return Promise.resolve().then(() => Date.now() - now < 300 && executeFutureDataWithMicrotasksOnly());
}
function blockfor300ms() {
for (let i = 0; i < 300; i++) {
// just delaying logic
}
}
// this sets the printHello function in callback queue { event loop queue }
setTimeout(printHello, 0);
const futureData = executeFutureDataWithMicrotasksOnly();
// after promise is resolved display function gets added into other queue : Microtask queue { job queue}
futureData.then(display);
// event loop gives prefrence to Microtask queue ( untill its complete)
blockfor300ms();
// which runs first
console.log('Me first !')
&#13;
从上面的示例中可以看出,如果使用仅具有微任务的方法替换fetch
,则执行顺序会按预期更改,但fetch
和executeFutureDataWithMicrotasksOnly
都会执行类似的时间间隔。当futureData
不再对任务进行排队时,包括display
在内的所有微任务都将在当前正在执行的任务结束时执行,这是任务printHello
的上一个任务。
答案 2 :(得分:0)
我认为问题出在以下事实:您的函数blockfor300ms
阻塞线程的时间不足以使提取操作接收到响应。
当事件循环看到它可以调用printHello
时,作业队列中还没有任何内容。
答案 3 :(得分:0)
这将帮助您更好地理解 JavaScript 的工作原理。
在这种情况下,fetch(Promise
) 花费的时间比 setTimeout
多,因此当事件循环周期运行时,fetch(Promise
) 仍在进行中,而 {{1} } 首先执行,因为它花费的时间更少,并从任务队列中出来并被处理,当 fetch(setTimeout
) 结束时,它从微任务队列中出来。
如果你增加 Promise
时间,那么第一个 fetch(setTimeout
) 将首先发生,然后 setTimeout 将发生。希望这能解决您的问题。