请指出我在学习异步 Javascript 中的错误 [英] Please point out the misfacts in my learning regarding Asynchronous Javascript

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

问题描述

我是 Javascript 和异步编程的新手,有些东西让我感到困惑,请指出我学习中的错误.

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

  • 异步函数的回调被放入消息队列中通过事件循环执行.
  • 异步执行是非阻塞的,完成通过事件循环.
  • setTimeout 这样的函数是异步的.
  • 异步函数是阻塞的,只有它们的回调函数是阻塞的非阻塞.
  • Callbacks of an Asynchronous function are put in a message queue and executed via event loop.
  • Asynchronous execution is non-blocking, done via event loop.
  • Functions like setTimeout are asynchronous.
  • Asynchronous functions are blocking, only their callbacks are non-blocking.

以上内容如有错误,请详述.

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

推荐答案

异步函数的回调被放入消息队列并通过事件循环执行.

Callbacks of an Asynchronous function are put in a message queue and executed via event loop.

没有

通常有两种异步函数 - 一种在完成时采用某种回调来运行,另一种则产生 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.

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

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() 方法接受一个回调,并在承诺得到解决后执行它,这也发生在异步操作完成之后.因此,我们可以按照正确的顺序一起执行:

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 是一种回调,因为它们在这里取代了它们并且以大致相同的方式执行.当某些事情成功或失败时,您仍然传递要执行的回调.但它们不只是回调.

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.

无论如何,给 Promise 的回调仍然延迟:

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:

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

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

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
*/

无论如何,我认为基于 Promise 的函数通过回调工作是不舒服的.是的,但在还原论的意义上.

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.

异步执行是非阻塞的,通过事件循环完成.

Asynchronous execution is non-blocking, done via event loop.

没有

首先,让我们明确这一点 - 阻塞行为是当环境忙于执行某事时.通常在该执行期间没有其他代码可以运行.因此,进一步的代码被阻止运行.

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.

我们以这段代码为例:

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 executors(给予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.

全身都在阻塞:

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
*/

澄清一点,任何值都可以等待,而不仅仅是承诺.等待一个 non-promise 仍然会暂停并恢复执行,但由于没有什么可等待的,这将导致它成为微任务队列中的第一件事.

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.

无论如何,执行异步函数的主体可能会阻塞.取决于操作是什么.

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 + "
" + 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 这样的函数是异步的.

Functions like setTimeout are asynchronous.

是的.

虽然,它可能正在考虑什么是like 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.

异步函数是阻塞的,只有它们的回调是非阻塞的.

Asynchronous functions are blocking, only their callbacks are non-blocking.

没有

如上所述,异步函数可能阻塞.取决于他们具体做什么.例如 $.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 将不会被安排在以后,但将成为处理该结果的同步代码序列的一部分.因此,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 中的错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆