【问题标题】:Is Javascript async and await the equivalent to mulithreading?Javascript async 和 await 是否等同于多线程?
【发布时间】:2020-11-19 00:45:48
【问题描述】:

是用asyncawait粗人的线程吗?

很多个月前,我学会了如何在 Android 上编写多线程 Java 代码。我记得我必须创建线程、启动线程等。

现在我正在学习 Javascript,我刚刚了解了 asyncawait

例如:

async function isThisLikeTwoThreads() {
 const a = slowFunction();
 const b = fastFunction();
 console.log(await a, await b);
}

这看起来比我以前做的简单得多,而且更直观。

slowFunction() 将首先启动,然后 fastFunction() 将启动,console.log() 将等到两个函数在记录之前解决 - 并且 slowFunction()fastFunction() 可能同时运行。我希望它们最终在浏览器上,无论这些线程是否是单独的线程。但它看起来像粗略的多线程一样走路和说话。是吗?

【问题讨论】:

  • Javascript 是单线程语言
  • 不是,不是。 asyncawait 基本上只是语法糖,用于构建基于生成器的机制来管理 Promise 实例。
  • 如果slowFunction()fastFunction() 是异步函数,则假定它们被挂起,等待信号重新进入事件循环。它们不是在单独线程中运行的受 CPU 限制的函数。
  • @Pointy - 在进行函数调用时,您不必await。例如,在上面的 OP 代码中,如果 slowFunctionfastFunction 都启动异步进程,则上面的代码会同时启动并允许它们并行运行,然后等待它们都完成后再执行 console.log。如果 OP 使用了const a = await longFunction(); const b = await slowFunction();,它们将一个接一个地完成; fastFunctionslowFunction 完成之前不会开始其进程。我肯定在真实代码中做到了这一点(不仅仅是Promise.all :-))。
  • @T.J.Crowder 好吧,我猜是的,它可以是任何表达式,并且当表达式计算为 Promise 时,它​​的行为“类似于 Promise”。我不能在我的环境中使用async/await,所以我尽量保持简单。

标签: javascript multithreading asynchronous


【解决方案1】:

我建议您阅读此内容以了解答案是否定的:https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

总而言之,运行时使用多个线程进行内部操作(网络、磁盘...用于 Node.js 环境,渲染、indexedDB、网络...用于浏览器环境)但您编写的 JavaScript 代码和一个您从不同的库导入将始终在单个线程中执行。异步操作会触发回调,回调会被排队并一一执行。

执行这个函数时基本上会发生什么:

async function isThisLikeTwoThreads() {
 const a = slowFunction();
 const b = fastFunction();
 console.log(await a, await b);
}

执行slowFunction。 执行fastFunction。 当 a promise 和 b promise 已解决时,将其余代码 (console.log(await a, await b)) 排入队列。 console.log 在同一线程中运行 isThisLikeTwoThreads 返回之后,并且可能的排队回调已经返回。假设 slowFunctionfastFunction 都返回承诺,这相当于:

function isThisLikeTwoThreads() {
 const a = slowFunction();
 const b = fastFunction();
 a.then(aResult => b.then(bResult => console.log(aResult, bResult)));
}

【讨论】:

  • 将您的承诺和解决的值命名为相同有点令人困惑。虽然它在功能上等同于您编写的内容(除了忘记返回Promise.all() 表达式,并返回由slowFunction()fastFunction() 同步抛出的错误作为被拒绝的承诺),它实际上是一个“严格”等价于:return Promise.resolve(a).then(_a => Promise.resolve(b).then(_b => { console.log(_a, _b); }));
  • 如果 ab 是本机承诺对象,那么这是正确的,否则 Promise.resolve() 不是空操作。我想为了简单起见,它已经足够接近了,我只是不想在实际上不是真的时使用“严格”这个词。
  • 是的,但是如果 OP 在他们面前写了await,假设他们返回了Promise。我会澄清的
  • await 实际上确实插入了一个明确的 Promise.resolve() 调用以将 thenable 转换为 Promise (step 2 of 6.2.3.1 await) edit 随意忽略这个,刚刚看到你删除“严格”
  • 我知道是这样,我的目标只是编写等效的简单代码,以防 ab 包含原生承诺
【解决方案2】:

是用asyncawait粗人的线程吗?

不,一点也不。它只是语法糖(真的,真的有用的糖),而不是使用承诺,而这又只是一种(真的,真的有用)使用回调的正式方式。它很有用,因为您可以异步等待(不阻塞 JavaScript 主线程)本质上是异步的(如 HTTP 请求)。

如果您需要使用线程,请使用web workersNode.js worker threads 或您的环境提供的任何多线程。根据规范(现在),一次只允许一个线程在给定的 JavaScript“领域”内工作(非常松散:代码运行的全局环境及其关联对象等)。 ) 因此一次只有一个线程可以访问该领域内的变量等,但线程可以通过消息传递(包括在它们之间传输对象而不进行复制)和shared memory 进行合作。

例如:

async function isThisLikeTwoThreads() {
 const a = slowFunction();
 const b = fastFunction();
 console.log(await a, await b);
}

以下是调用 isThisLikeTwoThreads 时该代码的作用:

  1. slowFunction同步调用,其返回值被赋值给a
  2. fastFunction同步调用,其返回值被赋值给b
  3. isThisLikeTwoThreads 到达await a 时,它将a 包装在一个promise 中(就像你做了Promise.resolve(a))并返回一个新的promise(不是同一个promise)。让我们将包裹在a 周围的promise 称为“aPromise”,并将函数返回的promise 称为“functionPromise”。
  4. 稍后,当aPromise结算时,如果被拒绝functionPromise以相同的拒绝原因被拒绝,并跳过以下步骤;如果满足了,就完成下一步
  5. isThisLikeTwoThreads 中的代码继续将 b 包装在一个承诺 (bPromise) 中并等待它解决
  6. bPromise结算时,如果被拒绝functionPromise以同样的拒绝理由被拒绝;如果已完成,isThisLikeTwoThreads 中的代码将继续记录 aPromisebPromise 的完成值,然后用值 undefined 完成 functionPromise

上述所有工作都是在调用isThisLikeTwoThreads 的JavaScript 线程上完成的,但它分布在多个“作业”(JavaScript 术语;HTML 规范称它们为“任务”并指定@987654324 @ 了解它们在浏览器上的处理方式)。如果slowFunctionfastFunction 启动了一个异步进程并为此返回了一个promise,则该异步进程(例如,浏览器执行的HTTP 调用)可能会在JavaScript 线程执行其他操作时与JavaScript 线程并行继续或者(如果它也是主线程上的 JavaScript 代码)可能已经竞争了 JavaScript 线程上的其他工作(通过将作业添加到作业队列并在循环中处理它们的线程来竞争)。

但是使用 Promise 不会增加线程。 :-)

【讨论】:

  • (FWIW,我在我的新书中详细介绍了上述内容。Promise 在第 8 章中,async/await 在第 9 章中,共享内存在第 16 章中。请参阅我的如果您有兴趣,请提供链接的个人资料。)
  • 不错的答案@T.J. Crowder,是否可以肯定地说 async/await 是 JavaScript 中的并发模型,尽管它不一定是多线程的? - 试图绕过它,谢谢
  • @phoenixdown - 这是一个有趣的问题。我猜就是这样。例如,in this fiddleab 肯定是同时运行的。所以是的,我会说concurrency不是 parallelism,因为虽然ab 重叠,但它们不会在同一时刻做某事。 (为此,您需要一个工作线程。)但请注意,我没有接受过正规的计算机科学教育。但就我对这些术语的理解而言,是的,我认为这是正确的。
【解决方案3】:

相似但不一样。 Javascript一次只会做“一件事”。它基本上是单线程的。也就是说,它可能看起来很奇怪,因为结果可能会以不同的顺序到达 - 诸如 async 和 await 之类的功能会加剧这种情况。

例如,即使您以 70fps 的速度渲染后台进程,在可用于完成 Promise 或接收事件通知的渲染逻辑中也存在小的差距——正是在这些时刻完成 Promise,从而产生多线程的错觉。

如果你真的想锁定浏览器,试试这个:

let a = 1;

while (a == 1){
    console.log("doing a thing");
}

你永远不会让 javascript 和 chrome 或任何会毁掉你的脚本的东西再次工作。原因是当它进入该循环时 - 没有任何东西会再次触及它,不会呈现任何事件,也不会传递任何承诺 - 因此,单线程。

如果这是一个真正的多线程环境,您可以通过将变量更改为新值来从外部打破该循环。

【讨论】:

    猜你喜欢
    • 2012-02-04
    • 2020-07-26
    • 2012-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    相关资源
    最近更新 更多