本文介绍了请指出我在学习异步Javascript时的错误事实的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Java语言和异步编程的新手,有一些使我感到困惑的地方,请指出我在学习中的错误之处.

I am new to Javascript and Ascynchronous programming, and a few things confuse me, please point out the misfacts in my learning.

  • 异步函数的回调被放入消息队列中,并且通过事件循环执行.
  • 异步执行是非阻塞的,已完成通过事件循环.
  • setTimeout 之类的功能是异步的.
  • 异步函数正在阻塞,只有它们的回调是非阻塞.

如果以上任何错误,请详细说明.

If any of the above are wrong, please elaborate it.

推荐答案

否.

通常有 两种异步函数-完成后需要运行某种回调才能运行,结果产生Promise的异步函数.

There are generally two kinds of asynchronous functions - ones that take some sort of callback to run when they are done and ones that produce a Promise as a result.

这是基于回调的函数的示例:

This is an example of a callback-based function:

setTimeout(() => console.log("foo"), 0);

console.log("bar");

/* output: 
bar
foo
*/

setTimeout 将延迟稍后执行的回调.即使超时为零毫秒,它仍将回调调度到当前执行完成的之后,因此顺序为:

setTimeout will delay a callback to be executed later. Even with a timeout of zero milliseconds, it still schedules the callback until after the current execution is complete, so the order is:

  1. setTimeout 安排回调.
  2. console.log("bar")运行.
  3. 运行带有 console.log("foo")的回调.
  1. setTimeout schedules the callback.
  2. console.log("bar") runs.
  3. The callback with console.log("foo") runs.

没有消息队列,有一个 tasks 队列可以执行.简要概述一下,事件循环从队列中提取一个任务,然后执行它,直到完成,然后执行下一个任务,并执行,直到完成,等等.一个简单的伪代码表示为:

There isn't a message queue, there is a queue of tasks to execute. As a brief overview, the event loop takes one task from the queueand executes it to completion, then takes the next one and executes it to completion, etc. A simple pseudo-code representation would be:

while(true) {
    if (eventQueue.hasNext()) {
        task = eventQueue.takeNext();
        task();
    }
}

有关事件循环的更多信息,请参见此处.

话虽这么说,与许多其他基于回调的函数相比,事件循环对 setTimeout 的影响更大.基于回调的函数的其他示例是jQuery的 $.ajax()或用于文件系统访问的Node.js fs 模块(非承诺API).他们和其他人也采取了稍后将执行的回调,但其限制因素不是事件循环,而是函数执行的任何基础操作.对于 $.ajax() 何时,何时将调用回调取决于网络速度,延迟和响应请求的服务器.因此,虽然回调 将由事件循环执行,但其他所有事情也是如此.因此,这并不特殊.这里只有 setTimeout 具有更特殊的交互作用,因为根据可用的任务,时间安排可能不精确-如果您安排某项任务在10毫秒内运行,而某些任务需要12毫秒才能完成,则回调由 setTimeout 不会准时运行.

With all that said, the event loop has more bearing on setTimeout than many other callback-based functions. Other examples of callback based functions are jQuery's $.ajax() or the Node.js fs module for file system access (the non-promise API for it). They, and others, also take a callback that will be executed later but the bounding factor is not the event loop but whatever underlying operation the function makes. In the case of $.ajax() when the callback would be called depends on network speed, latency, and the server which responds to the request. So, while the callback will be executed by the event loop, so is everything else. So, it's hardly special. Only setTimeout has a more special interaction here, since the timings might be imprecise based on what tasks are available - if you schedule something to run in 10ms and some task takes 12ms to finish, the callback scheduled by setTimeout will not run on time.

这里是一个例子:

async function fn(print) {
  await "magic for now";
  console.log(print);
}

fn("foo")
  .then(() => console.log("bar"));

/* output: 
foo
bar
*/

(为便于说明,我现在省略了很多细节.)

(I'm omitting a lot of details for now for illustration.)

从技术上讲,承诺是为处理异步操作而设计的回调的抽象.promise的 .then()方法进行回调,并在promise被解析后的之后执行.在异步操作完成后也会发生.因此,我们可以按正确的顺序将执行序列在一起:

Promises are technically an abstraction over callbacks tailored towards handling asynchronous operations. The .then() method of a promise takes a callback and will execute it after the promise gets resolved, which also happens after the asynchronous operation finishes. Thus, we can sequence together execution in the right order:

async function fn(print) {
  await "magic for now";
  console.log(print);
}

fn("foo");
console.log("bar");

/* output: 
bar
foo
*/

从某种意义上说,promise是一种回调,因为它们是在这里替换它们并以大致相同的方式执行.您仍然会传递成功或失败时要执行的回调.但它们不是 just 回调.

In a way, promises are sort of callbacks because they are here to replace them and do it in broadly the same fashion. You still pass callbacks to be executed when something succeeds or fails. But they aren't just callbacks.

无论如何,给予承诺的回调会仍然延迟:

At any rate, a callback given to a promise is still delayed:

Promise.resolve()
  .then(() => console.log("foo"));
  
console.log("bar");


/* output: 
bar
foo
*/

但不能通过与 setTimeout 所使用的事件队列相同的事件队列.有两个队列:

But not via the same event queue as the one setTimeout uses. There are two queues:

  • 宏任务队列- setTimeout 将内容放入其中,并且所有UI交互也都在此处添加.
  • 微任务队列-承诺在这里安排他们的事情.
  • macrotasks queue - setTimeout places things in it, and all UI interactions are also added here.
  • microtasks queue - promises schedule their things here.

当事件循环运行时,微任务队列(因此​​,promise)具有最高优先级,然后是宏任务队列.这导致:

When the event loop runs, the microtasks queue (so, promises) has the highest priority, then comes the macrotask queue. This leads to:

setTimeout(() => console.log("foo")); //3 - macrotask queue

Promise.resolve()
  .then(() => console.log("bar")); //2 - microtask queue
  
console.log("baz"); //1 - current execution


/* output: 
baz
bar
foo
*/

无论如何,我不满意地说基于承诺的函数通过回调起作用.是的,但在还原主义意义上.

At any rate, I don't think I'm comfortable saying that promise-based functions work via callbacks. Sort of yes but in a reductionist sense.

否.

首先,让我们弄清楚这一点-阻塞行为是环境忙于执行某些操作时的行为.通常在执行期间没有其他代码可以运行.因此,阻止的其他代码无法运行.

First of all, let's make this clear - blocking behaviour is when the environment is busy executing something. Usually no other code can run during that execution. Hence, further code is blocked from running.

让我们以下面的代码为例:

Let's take this code as example:

setTimeout(taskForLater, 5000);

while (somethingIsNotFinished()) {
    tryToFinish();
}

此处 taskForLater 将计划在5秒钟内运行.但是, while 循环将阻止执行.由于没有其他代码可以运行,因此 taskForLater 可能会在10秒内运行,如果这是完成循环所需的时间.

Here taskForLater will be scheduled to run in 5 seconds. However, the while loop will block the execution. Since no other code will run, taskForLater might run in 10 seconds time, if that's how lot it takes for the loop to finish.

运行异步函数并不总是意味着它与当前代码并行运行 .在大多数情况下,环境一次执行一件事.JavaScript默认没有多线程,并行执行是一种选择行为,例如,通过使用工人.

Running an asynchronous function doesn't always mean it runs in parallel with the current code. The environment executes one thing at time in most cases. There is no multi-threading by default in JavaScript, parallel execution is an opt-in behaviour, for example by using workers.

这可能意味着两件事,目前尚不清楚您引用的是哪一个:

This can mean a couple of things and it's not clear which one you reference:

  1. 在异步函数的主体中运行
  2. 等待直到基础异步操作完成

在两种情况下,引用的语句都是错误的,但原因不同.

In both cases the quoted statement is wrong but for different reasons.

异步函数是对Promise的语法糖.他们使用相同的机制,但是只是以不同的方式呈现代码.但是,异步功能正在阻止.作为附带说明,promise执行程序(给予promise构造函数的回调)也是如此.在这两种情况下,该函数都会运行并阻塞,直到某些原因导致其暂停.演示该功能的最简单方法是使用异步功能-使用 await 暂停该功能的执行,并安排其余功能在以后完成.

Async functions are syntactic sugar over promises. They use the same mechanism but just present the code differently. However, an async function is blocking. As a side note, so are promise executors (the callback given to a promise constructor). In both cases, the function will run and block until something causes it to pause. The easiest way to demonstrate it is with an async function - using await will pause the execution of the function and schedule the rest of it to be finished later.

整个身体都处于阻塞状态

Whole body is blocking:

async function fn() {
  console.log("foo");
  
  console.log("bar");
}


fn();
console.log("baz");

/* output:
foo
bar
baz
*/

中间暂停:

async function fn() {
  console.log("foo");
  await "you can await any value";
  console.log("bar");
}


fn();
console.log("baz");

/* output:
foo
baz
bar
*/

为澄清起见,可以等待任何任何值,而不仅仅是承诺.等待未承诺仍然会暂停并继续执行,但是由于没有什么可等待的,因此这将使其成为微任务队列中的第一件事.

As a point of clarification, any value can be awaited, not just promises. Awaiting a non-promise will still pause and resume the execution but since there is nothing to wait for, this will cause it to be among the first things on the microtask queue.

无论如何,执行异步功能 might 块的主体.取决于操作是什么.

At any rate, executing the body of an async function might block. Depends on what the operation is.

在谈论底层操作"时,大多数时候我们是指我们将控件移交给其他东西,例如浏览器或库.在这种情况下,当前的JavaScript环境无法完成该操作,我们调用将执行操作的操作,并且仅在完成后通知当前的JavaScript环境.例如,在调用 fetch 的浏览器中,将触发网络请求,但这是由浏览器处理的,而不是我们的代码.因此,它是非阻塞的,但不受执行环境外部的限制.

When talking about "underlying operation", most of the times we mean that we hand off the control to something else, for example the browser or a library. In this case, the operation is not fulfilled by the current JavaScript environment we call something which will perform an operation and only notify the current JavaScript environment when it finishes. For example, in a browser calling fetch will fire off a network request but it's the browser handling it, not our code. So it's non-blocking but not by outside the execution environment.

fetch("https://official-joke-api.appspot.com/random_joke")
  .then(res => res.json())
  .then(joke => console.log(joke.setup + "\n" + joke.punchline));

console.log("foo");

话虽如此,我们甚至无法概括给定异步操作将执行的操作.它实际上可能至少在一段时间内阻止了执行,或者可能完全在与当前JavaScript环境不同的过程中执行.

With that said, we cannot even generalise what a given asynchronous operation will do. It might actually block the execution, at least for a while, or it might be entirely carried out in a separate process to the current JavaScript environment.

是的.

尽管如此,它可能正在考虑类似 setTimeout 的内容.是 setInterval 吗?它是任何基于回调的异步函数吗?问题是该定义开始变成循环的基于异步回调的函数,例如 setTimeout 是异步的".

Although, it's probably considering what is like setTimeout. Is it setInterval? Is it any callback-based asynchronous function? The problem is that the definition starts to become circular "asynchronous callback-based functions like setTimeout are asynchronous".

并非每个接受回调的函数都是异步的.那些被认为是可能被认为类似于 setTimeout .

Not every function that takes a callback is asynchronous. Those that are might be considered similar to setTimeout.

否.

如上所述,异步功能可能会阻塞.取决于他们到底做什么.例如, $.ajax 将使用浏览器启动网络请求,类似于 fetch .对于 $.ajax ,该函数在准备请求时阻止,但在发送请求后不会阻止.

As discussed above, asynchronous functions might block. Depends on what exactly they do. For example $.ajax will initiate a network request using the browser, similar to fetch. In the case of $.ajax the function blocks while preparing the request but not after it's sent.

该语句的第二个问题是调用回调非阻塞是不正确的-执行它们肯定会再次阻塞执行.回调是普通的JavaScript代码,时间到时将通过事件循环执行.在任务运行完成时,执行仍然被阻止.

The second problem with the statement is that it's incorrect to call the callbacks non-blocking - executing them will certainly block the execution again. The callback is a normal JavaScript code that will be executed via the event loop when its time comes. While the task is running to completion, execution is still blocked.

如果从回调将被放入事件队列的意义上说,它们是非阻塞的,那还是不能保证.考虑以下示例代码:

If you mean that they are non-blocking in the sense that the callback will be put on the event queue, that's still not guaranteed. Consider the following illustrative code:

function myAsyncFunction(callback) {
    const asyncResult = someAsynchronousNonBlockingOperation();
    
    doStuff(result);
    callback(result);
    doMoreStuff(result);
}

一旦 someAsynchronousNonBlockingOperation()产生一个执行 callback 的值,该值将不会在以后安排,但它将成为处理结果的同步代码序列的一部分.因此,回调将在以后执行,但它本身不是一项任务,而是包含 doStuff 的整体任务的一部分doMoreStuff .

Once someAsynchronousNonBlockingOperation() produces a value executing callback will not be scheduled for later but will be part of the synchronous sequence of code that processes that result. So, callback will be executed later but it will not be a task by itself but part of an overall task that encompasses doStuff and doMoreStuff.

这篇关于请指出我在学习异步Javascript时的错误事实的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-01 01:59