【问题标题】:If we typically need a promise to be handled before moving on, why not return an already-handled response instead of a promised response?如果我们通常需要在继续之前处理一个承诺,为什么不返回一个已经处理的响应而不是一个承诺的响应呢?
【发布时间】:2020-04-27 19:09:13
【问题描述】:

我已经阅读了很多内容并观看了视频,但遗憾的是还没有找到我的问题的答案。这让我觉得我的问题被误导了,但我想知道为什么。 (注意:我确实发现 this question 很有帮助,但答案似乎没有解决我的好奇心。)


如果我理解正确的话,promise 的作用并不是这样的:

朋友:听着,到最后,我会给你买一台电脑。这是一个承诺。

我:太棒了!当你这样做的时候,我会做很多其他的事情。

朋友(在一天结束时):这是这个盒子。你只需要打开(处理)它。

我打开盒子,如果它是空的,我知道她的承诺已经失败(被拒绝)。如果里面有电脑,我知道她的承诺是成功的。而且,如果里面有一张纸条,上面写着“仍在努力履行这个承诺”,我知道她的承诺仍未完成。


相反,promise 的功能似乎是这样的:

朋友:听着,到最后,我会给你买一台电脑。这是一个承诺。

我:好的,我希望尽快使用那台计算机,因为我今天的所有任务都取决于它。我需要发送几封电子邮件,这将需要那台计算机。我需要研究获取请求,这也需要您的承诺。然后我需要在 StackOverflow 上问一个问题,这肯定需要那台机器。所以,我只是等到你的承诺成功或失败(或者你告诉我你需要更多时间)。

然后,朋友带着第一个场景中的同一个盒子回来,所以我需要先打开盒子(或处理承诺),然后才能执行任何进一步的任务。


那么,我的问题是,如果我们需要等待一个承诺响应,为什么该响应没有预先处理好?换句话说,为什么我的朋友递给我一个我必须打开的盒子,而不是递给我电脑或告诉我他们失败了(或者让我知道他们需要更多时间)?

当我发出提取请求时,我不能只使用响应。我需要先json() 它。或者,我需要先then()catch()。为什么我们返回承诺的数据而不是数据本身(当然,如果成功的话)?

我希望这个问题有意义。我期待我的假设得到纠正。

【问题讨论】:

  • 您无法返回数据本身。该函数在数据可用之前很久就返回了。这就是 Javascript 中异步操作的工作方式。所以,在函数返回的时候,数据还没有到达。因此,您返回一个承诺,允许调用者将 .then() 侦听器挂接到该承诺,并在数据实际准备好时收到通知。
  • "当我发出获取请求时,我不能只使用响应。我需要先json()" 但是如果你正在获取图像怎么办还是纯文本?你为什么要打电话给.json() 并得到一个错误? 是你不希望它预先处理的原因 - fetch知道你想对数据做什么。即使您正在获取 JSON,出于某种原因,您甚至可能仍希望它采用文本格式。例如,也许您只是获取一个 .json 文件并将其存储在数据库中。
  • @VLAZ,我并不是要暗示每个响应都会通过 .json(),但是在重新阅读我的问题之后,这并不清楚。无论哪种方式,您的观点都是绝对有效的,谢谢!

标签: javascript promise fetch


【解决方案1】:

不,你的比喻不合适。没有“一天结束”,也没有“我需要打开盒子来检查它是否为空”。

承诺是你的朋友告诉你“我需要更多时间,但我保证最终会得到这个”。也许更重要的是,“完成后我会告诉你”。如果你想用一个盒子来比喻,它更像是一个盒子,上面有一张纸条,一旦里面有一台电脑,它就会自动打开。没有重复检查。

当然,您可以坐在那里什么也不做,只等盒子打开。但是被递给承诺箱意味着你的朋友不能立即把计算机递给你,而是需要更多的时间,所以你应该同时做一些其他的事情。那可能是向其他朋友要更多电脑。或者让本地图书馆为您提供一本关于要阅读的承诺书,以便您在拿到计算机后立即开始编程(无需等待对 SO 问题的回复)。当然,这意味着您将向您的客户承诺您的工作结果,这样即使无事可做,只能等待被提取的计算机,他们 可能。

【讨论】:

  • 要详细说明计算机的比喻——正如Bergi 所说,当您获得一台计算机时,您会收到通知。您已经制定了任务 - 1. 写电子邮件 2. 研究fetch 3. 在 SO 上发帖。确实也没有连续检查盒子 - 你不只是站在盒子周围,每隔一段时间偷看一下里面。完成后,您的朋友会拍拍您的肩膀说“我完成了”(承诺已解决),然后您开始写电子邮件,然后进行研究,然后发布。或者你的朋友进来说“对不起,鲨鱼吃了你的电脑”(承诺被拒绝),你可能会尝试做某事(.catch
  • @Bergi,谢谢你的回答。我并不是要暗示我在反复检查该框,但是您的更正是有道理的。顺便说一句,当你说,“或者读一本关于承诺的书,这样你就不需要问 SO 问题了”,你的意思是什么?我知道我的问题被否决了(尽管我不确定为什么),但是您是否暗示我的问题浪费了宝贵的 SO 数据库空间?我真诚地问这个问题,因为我不认识你,并且更喜欢问而不是假设(当涉及到人类意图时)。无论哪种方式,我都真诚地感谢您抽出宝贵时间做出回应!
  • @MicahWierenga 不,这只是作为使用多种策略来实现相同结果的示例(此处:在网络上提问,或阅读有关该主题的书),然后使用结果最早成功的策略。 Promise 是处理此类并发任务结果的好方法。
  • @Bergi 有道理。再次感谢您的有益回复!
【解决方案2】:

当我发出提取请求时,我不能只使用响应。我需要先 json() 它。或者,我需要先 then() 和 catch() 它。为什么我们返回承诺的数据而不是数据本身(如果成功,当然)?

您不能返回数据本身。主机函数本身在数据可用之前很久就返回了。这就是非阻塞、异步操作在 Javascript 中的工作方式。所以,在函数返回的时候,数据还没有到达。因此,您返回一个承诺,允许调用者将.then() 侦听器挂接到承诺,并在数据实际准备好或.catch() 侦听器时收到通知以查看是否有错误。

让我们看一个简单的例子。假设您有一个异步操作返回一些值。您想创建另一个函数,将该值平方并返回它。

// get random number between 0 and 9999
function rand() {
     return Math.floor(Math.random() * 10000);
}

function getValue() {
     console.log("5");
     return new Promise((resolve, reject) => {
         console.log("6");
         setTimeout(() => {
             console.log("7");
             resolve(rand());                 
             console.log("8");
         }, 500);
     });
}

function getSquaredValue() {
     console.log("3");
     return getValue().then(val => {
         console.log("4");
         return val * val;
     });
}

console.log("1");
getSquaredValue().then(val => {
    console.log("got value");
}).catch(err => {
    console.log(err);
});
console.log("2");

运行该脚本的输出如下:

1
3
5
6
2
7
8
4
got value

研究此顺序以遵循这些异步操作的流程。它应该是 1、3、5、6,因为这是函数调用的顺序。

但是,现在看看接下来会发生什么。是 2。这意味着 getSquaredValue() 已经返回。但是,我们还没有我们的随机数或其平方结果。这就是为什么你需要承诺。您返回承诺,调用者附加他们的.then() 侦听器。在未来某个未知的时间,当该值终于可用时,它会通知.then()监听器并告诉你最终的值是多少。

由于您创建了一些类比,我将自己制作一个。假设您正在建造一个储藏室。您确定需要一把新锤子,因此您在亚马逊上订购了一把。

在我们的世界中,从亚马逊交付新包裹是非阻塞和异步的。您在星期一提交订单并指定一天交货。您不必整天坐在门口等待包裹而无所事事。取而代之的是,您提交订单并立即进行其他项目,吃饭等……就好像什么都没发生一样。然后,在后台(无需您进一步关注),采购该产品、将其装入盒子并发送给您的整个过程正在进行。

然后,稍后某个时间(您指定了次日送达,所以应该是次日某个时间),门铃响起,表示包裹送达。当您有时间在做其他事情时,您会从其他任务中休息一下,然后从门口取包裹。

异步操作现已完成。完成后它会通知您,然后您可以获取该值。同时,在处理订单的所有时间,您都可能在做其他事情(非阻塞)。

【讨论】:

  • 如果你想继续用锤子做类比:使用then 就像一个待办事项列表,在你得到游戏玩家后你会得到。 then(package => package.open()).then(openPackage => takeHammer(openPackage)).then(hammer => finishWork(hammer))。无论是立即发生还是更紧急的事情(妈妈打电话告诉你阿姨的狗的可爱把戏)打断了你,你都要完成待办事项并在分娩后尽快完成。
  • @jfriend00,谢谢!这是我在过去几年读到的最好的解释。我已经阅读了很多解释,我很高兴几乎所有这些解释都帮助了人们,但由于某种原因,他们只是没有为我点击。一旦您说“主机函数本身在数据可用之前很久就返回了”,这显然是我需要打开思路的关键。然后代码 sn-p 很棒,锤子隐喻现在将成为我的首选(因为我知道我自己的类比是有缺陷的,但只是不确定如何)。精心设计的答案,谢谢!
  • @VLAZ,感谢您的加入!我发现这个修改很有帮助(而且很有趣:-))。
【解决方案3】:

您的标准 html + javascript 网站有效地在一个线程中运行。长时间运行的 javascript 任务将阻止 ui 接收输入或呈现更新。

网络请求最多可能需要几秒钟。如果我们必须等待 3 秒钟的结果,那么网站将被冻结 3 秒钟。相反,Web 请求在内部由一个单独的线程处理,并且当 Web 请求完成(成功或失败)时通知主 javascript 线程。

所以我们不能等待请求,因为它会冻结网站,但我们还需要能够在网络请求到达时接收到它的结果。普通的旧原始 javascript 执行此操作的方式是通过 Callback functions。 这是一个基本示例,说明它如何处理来自here 的 Web 请求:

//This function is called when the web request completes
function reqListener () {
   console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();

现在,这与 Promise 有什么关系?好吧,很多人不喜欢回调,很容易纠缠不清。 Promise 是旧的基于回调的方法的有效包装器。当 Promise 解决所有 .then() 的所有问题时,Promise 也可以轻松传递给其他功能和服务。

所以我们不能只等待数据的原因是因为它会冻结网站。我们使用 Promise 的原因是因为它们是旧回调样式方法的便捷包装器。

如果您的网站需要/关键的 Web 请求怎么办?那么你仍然不需要阻止。只需显示一个加载屏幕,当 Promise 返回时,您可以显示您的预期数据,如果失败则显示错误消息。重要的是,当请求处于待处理状态时,即使没有太多可显示的内容,站点也会保持响应。鼠标点击和滚动将注册,加载微调器将动画等。

【讨论】:

    【解决方案4】:

    Javascript 是同步运行的,Promise 是一种处理异步操作的方法。 你如何使用承诺来处理它?在.then()里面。

     ... sync code
     promise.then((someVar)=>{
         async code will run when the promise is fulfilled and someVar will be defined
     })
     ... sync code will run before the promise is fulfilled and someVar is not defined
    
    

    您可以使用 async/await 语法将函数定义为异步函数,以实现您正在寻找的功能

    async function someAsyncFunction() {
       ... sync code
       let someVar = await promise();
       ... async code will run when the promise is fulfilled and someVar will be defined
    } 
    

    【讨论】:

      猜你喜欢
      • 2023-03-15
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      • 2021-04-10
      • 2014-02-15
      • 2015-11-27
      • 1970-01-01
      • 2015-06-05
      相关资源
      最近更新 更多