【问题标题】:Does promises only makes sense when the executor uses a web browser/runtime api? [duplicate]只有当执行程序使用 Web 浏览器/运行时 api 时,promise 才有意义? [复制]
【发布时间】:2022-01-07 10:42:35
【问题描述】:

一年多来,我一直在尝试完全理解 async 和 Promise 的概念,但我仍然无法掌握它的全部内容。

大多数 Promises 文档都是关于“非阻塞”的,示例代码通过这种方式模拟带有 setTimeout 的长请求:

console.log('foo1');
let promiseA = new Promise(resolv => {
  setTimeout(() => resolv('finished'), 1000);   
})
promiseA.then(answer => {
  console.log(answer);
});
console.log('foo2');

产生于

"foo1"
"foo2"
"finished"

但这不是同时混合了两个不同的概念吗?据我了解,setTimeout 函数是浏览器/运行时函数,setTimeout 精确触发 浏览器线程 中的内部计数器,您的回调函数在 重新添加到 JavaScript 引擎通过macrotask队列超时结束,从而让你下面调用的js函数依然执行。

但是没有使用 setTimeout 的例子呢?考虑一下:

let promiseA = new Promise(resolv => {
  uselesscounter = 0;
  for(let i = 0; i < 1000000000; i++) {
    uselesscounter+= 1;
  }
  resolv('finished');
})
promiseA.then(answer => {
  console.log(answer);
})
console.log('foo2')

从上面的例子中,我们可能认为我们将有“foo1”、“foo2”,然后循环将花费一些时间,我们将在几秒钟后“完成”。这不是正在发生的事情。由于执行程序代码是直接执行的(而不是使用任何浏览器 api)阻塞了下面的“foo2”,尽管最后解析回调被添加到微任务队列并在“foo2”之后显示.

那么,像这样不使用浏览器 api 函数的回调有意义吗?

【问题讨论】:

  • 不,promise executor 不会在“其他地方”运行。没有多线程或其他并行执行神奇地发生 - 如果你在执行器中阻塞线程,你就会阻塞线程。
  • 假设您使用“浏览器 api 函数”作为任何类型的异步主机函数的同义词,我想唯一的原因是如果您需要满足某些 API 要求,或者如果您构建一个您自己的 API期望最终是异步的。但在这些情况下,您也可以直接使用Promise.resolve(someValue)。在主线程上设置阻塞进程是没有意义的。
  • Promise 仅作为一种处理异步操作的方式真正“有用”,其中大部分工作可以委托给其他线程。工作线程、系统线程(通过运行时(浏览器/节点/等))或整个其他机器(例如通过 HTTP 请求)。如果您在 Promise 中所做的工作完全发生在 JS 中(而不是在 worker 中),那么该代码将在某个时候阻塞主线程。无论它是否在 promise 块内执行。
  • @VLAZ 超时计数器确实必须在 console.log('foo2');运行正确吗?我认为它是在浏览器中运行的,而不是在 javascript 中,就像一小块 C(或特定的浏览器语言)在内部进行倒计时,当它完成时,告诉 javascript“我完成了,你可以执行你的函数” (将其添加到宏任务队列中)
  • @YoannM whether or not an async task blocks the thread depends on the tasksetTimeout 永远不会阻塞。其他示例可能是 HTTP 调用或工作线程 - 如果操作在主线程之外完成,则它是非阻塞的。但是,如果您有一个循环,则不会因为将其放入 Promise 执行程序而从阻塞代码转换为非阻塞代码。

标签: javascript asynchronous promise callback


【解决方案1】:

在等待来自代码本身外部的某些数据或某种信号时使用 Promise 很有用,例如 api 调用等。

如果您的代码是完全独立的,那么使用 Promise 将不会提高性能。

在您的第二个示例中,代码按预期运行,因为承诺不会“稍后”执行。在 Promise 中,代码将继续运行,直到遇到未在当前上下文中执行的异步操作(线程、工作者等),当这种情况发生时,它不会在等待结果时阻塞,而是会运行承诺之后的行,直到请求承诺的结果(例如通过使用等待),当发生这种情况时,如果可用,您的运行时将返回承诺的结果,或者阻塞您的程序直到结果可用。

在您的情况下,承诺的内容都是同步的,因此是阻塞的。

【讨论】:

  • 好的,我想我已经掌握了窍门。但还是有些烦恼。我一定缺乏词汇。我们可以说我的承诺(第二个)是“同步的”,因为它没有调用异步函数吗?那么我们可以说一个承诺“是”(或不是)异步的吗?我认为从 Javascript 开发人员 only 的角度来看,我们不能编写“异步函数”是正确的,因为异步函数使用上下文之外的工作线程/线程?
  • @YoannM "我们能不能说一个承诺"是"(或不是)异步的" 我想说这是一个错误的问题。承诺只是某些任务的通知机制。它本质上不是同步的。尽管将它用于同步任务通常没有多大意义。作为一种通知机制,它不会告诉您有关底层任务是什么的任何信息。话虽如此,拥有一个返回承诺的同步任务很可能是一个错误或错误。在这些情况下,很少需要承诺。
  • @YoannM 对于术语,我会使用“立即解决的承诺”,其中“立即”表示“在同步代码中”。
  • @YoannM 使用承诺或异步函数只是告诉运行时(节点、浏览器等)该承诺的结果不是立即需要的,并且不会阻塞(等待)在当前上下文(线程)之外运行的 Promise 中的任何函数调用。因此,您编写的在当前上下文中运行的任何代码都将立即被评估,并且将该代码放入 Promise 或异步函数中不会改变这一点,它只会延迟返回该 Promise 的结果。因此,对 promise 的这种使用在性能方面毫无用处。
  • @YoannM 至于你的第二个问题,你可以编写一个真正的异步函数,例如使用超时。假设您期望创建一个文件,并且您想在创建该文件时读取该文件的内容,并且没有异步函数可以做到这一点。您将做出使用 setInterval 来检查文件是否存在的承诺,在找到文件时清除间隔并使用文件内容解决承诺。如果没有 Promise,即使结果不是立即需要,您也会阻塞其余代码,或者您将不得不使用回调。
猜你喜欢
  • 2012-12-07
  • 1970-01-01
  • 2021-10-07
  • 2019-01-18
  • 1970-01-01
  • 1970-01-01
  • 2014-10-23
  • 2011-10-26
  • 2019-03-25
相关资源
最近更新 更多