【问题标题】:What happens if you don't resolve or reject a promise?如果您不解决或拒绝承诺会发生什么?
【发布时间】:2016-08-12 14:33:18
【问题描述】:

我有一个场景,我正在返回一个承诺。 Promise 基本上是由 ajax 请求触发的。

在拒绝承诺时,它会显示一个错误对话框,提示存在服务器错误。

我想要做的是当响应代码是 401 时,我既不想解决承诺也不想拒绝它(因为它已经显示了错误对话框)。我想简单地重定向到登录页面。

我的代码如下所示:

function makeRequest(ur, params) {
  return new Promise(function (resolve, reject) {
    fetch(url, params).then((response) => {
      let status = response.status;    
      if (status >= 200 && status < 300) {
        response.json().then((data) => {
          resolve(data);
        });
      } else {
        if (status === 401) {
          redirectToLoginPage();
        } else {
          response.json().then((error) => {
            if (!error.message) {
              error.message = constants.SERVER_ERROR;
            }
            reject({ status, error });
          });
        }
      }
    });
  });
}

如您所见,如果状态为 401,我将重定向到登录页面。承诺既没有解决也没有被拒绝。

这段代码可以吗,还是有更好的方法来完成这个?

【问题讨论】:

    标签: javascript ecmascript-6 es6-promise


    【解决方案1】:

    ECMAScript 规范解释了 Promise 的用途和new Promise()

    Promise 是一个对象,用作延迟(也可能是异步)计算的最终结果的占位符。

    25.6.3.1 承诺(执行者

    注意 executor 参数必须是函数对象。调用它来启动并报告此 Promise 对象表示的可能延迟操作的完成。

    您应该使用承诺来获得未来的价值。此外,为了让您的代码简洁直接,您应该使用 Promise 来获取未来的值,而不是做其他事情。

    由于您还将程序控制流(重定向逻辑)混合到您的 Promise 的“执行程序”逻辑中,因此您的 Promise 不再是“计算结果的占位符”;相反,它现在是一个伪装成 Promise 的 JavaScript 小程序。

    因此,我建议不要将这个 JavaScript 程序包装在 new Promise() 中,而是像普通的 JavaScript 程序一样编写它:

    async function makeRequest(url, params) {
      let response = await fetch(url, params);
      let { status } = response;
      if (status >= 200 && status < 300) {
        let data = await response.json();
        successLogic(data);
      } else if (status === 401) {
        redirectToLoginPage();
      } else {
        let error = await response.json()
        if (!error.message) {
          error.message = constants.SERVER_ERROR;
        }
        errorLogic({ status, error });
      }
    }
    

    【讨论】:

      【解决方案2】:

      Promise 只是 Javascript 中具有属性的对象。它没有魔法。因此,未能解决或拒绝承诺只是无法将状态从“待处理”更改为其他任何内容。这不会在 Javascript 中引起任何基本问题,因为 Promise 只是一个常规的 Javascript 对象。如果没有代码保留对 Promise 的引用,则 Promise 仍将被垃圾回收(即使仍处于挂起状态)。

      这里的真正后果是,如果承诺的状态从未改变,这对承诺的消费者意味着什么?任何用于解决或拒绝转换的 .then().catch() 侦听器都不会被调用。大多数使用 Promise 的代码都希望它们在未来的某个时间解决或拒绝(这就是首先使用 Promise 的原因)。如果他们不这样做,那么该代码通常永远无法完成其工作。

      您可能拥有一些其他代码来完成该任务的工作,并且承诺只是被放弃而没有做任何事情。如果你这样做的话,Javascript 中没有内部问题,但这不是 Promise 的设计方式,通常也不是 Promise 的消费者期望它们如何工作的方式。

      如您所见,如果状态为 401,我正在重定向到登录页面。 承诺既没有解决也没有拒绝。

      这段代码可以吗?或者有没有更好的方法来做到这一点。

      在这种特殊情况下,一切正常,重定向是一种特殊且独特的情况。重定向到新的浏览器页面将完全清除当前页面状态(包括所有 Javascript 状态),因此可以使用重定向的快捷方式并留下其他未解决的问题。当新页面开始加载时,系统将完全重新初始化您的 Javascript 状态,以便清除任何仍待处理的承诺。

      【讨论】:

      • 正如您所提到的,重定向到新的浏览器页面将完全清除当前页面状态(包括所有 Javascript 状态)。但是如果我在单页应用程序中重定向会发生什么。我正在使用 ReactJS。所以不会有新的浏览器页面,只是不同的视图。在这种情况下,通过重定向走捷径并留下其他未解决的问题是否完全可以。
      • @Aniket - 那是你的问题没有描述的另一种情况。你必须确保事情被正确清理,这样你就没有任何内存泄漏。我不知道这是否会在反应中出现问题。下次请在您的问题中输入此类信息。
      • 确保你没有引用 rejectresolve 函数,而不仅仅是停止引用承诺。然后,GC 应该介入。否则,例如,如果您的代码仍然引用 resolve,那么 Promise 将不得不保留以防您的代码调用 resolve()。下面是一个 Web API 示例,如果不小心可能会泄漏 Promise:github.com/w3c/webcomponents/issues/674
      • 对于处理 401 时的单页应用程序场景,您可以像下面@CodingIntrigue 提到的那样“返回”。当然你需要 if(typeof json === "undefined") 检查但是 makeRequest 的调用者应该检查他们是否有好的数据。呼叫者永远不应盲目相信他们拥有良好的数据。对于 401,我不会拒绝承诺。对我的拒绝承诺意味着应该记录一个错误......这不是真正的错误,而是一个简单的重定向。
      • 值得注意的是,如果你正在运行节点并且你有悬空的 Promise,即使它应该完成,它们也会阻止你的节点进程退出,至少在 Promise 获得 GC 之前'd(它可能不会,因为如果它基本上完成了就不会分配新的内存!)
      【解决方案3】:

      我认为“如果我们不解决拒绝会发生什么”已得到很好的回答 - 您可以选择添加 .then.catch

      但是,这段代码可以吗?或者有没有更好的方法来完成这个。我想说有两点:

      当没有必要并且 fetch 调用可能失败时,你在 new Promise 中包装了一个 Promise,你应该采取行动,这样你的调用方法就不会坐下来等待一个永远不会被解决的承诺。

      这是一个示例(我认为这应该适用于您的业务逻辑,而不是 100% 肯定):

      const constants = {
        SERVER_ERROR: "500 Server Error"
      };
      function makeRequest(url,params) {
        // fetch already returns a Promise itself
        return fetch(url,params)
              .then((response) => {
      
                  let status = response.status;
      
                  // If status is forbidden, redirect to Login & return nothing,
                  // indicating the end of the Promise chain
                  if(status === 401) {
                    redirectToLoginPage();
                    return;
                  }
                  // If status is success, return a JSON Promise
                  if(status >= 200 && status < 300) {
                    return response.json();
                  }
                  // If status is a failure, get the JSON Promise,
                  // map the message & status, then Reject the promise
                  return response.json()
                    .then(json => {
                      if (!json.message) {
                          json.message = constants.SERVER_ERROR;
                      }
                      return Promise.reject({status, error: json.message});
                    })
              });
      }
      // This can now be used as:
      makeRequest("http://example", {})
        .then(json => {
          if(typeof json === "undefined") {
            // Redirect request occurred
          }
          console.log("Success:", json);
        })
        .catch(error => {
          console.log("Error:", error.status, error.message);
        })
      

      相比之下,使用以下方式调用您的代码:

      makeRequest("http://example", {})
        .then(info => console.log("info", info))
        .catch(err => console.log("error", err));
      

      不会记录任何内容,因为对http://example 的调用将失败,但catch 处理程序将永远不会执行。

      【讨论】:

      • 我只是想避免 if(typeof json === "undefined") { // 发生重定向请求 } 许多其他函数调用 makeRequest 的原因,我必须在每个调用它的函数。
      • 我不确定我是否遵循您的代码示例。那么对于这两个示例,都不会调用 makeRequest(..).catch 吗?那正确吗?这就是它读给我的方式。似乎您需要在原始 fetch 调用中捕获并让它返回一个被拒绝的承诺。
      【解决方案4】:

      正如其他人所说,如果您不解决/拒绝承诺,这确实不是问题。无论如何,我会以不同的方式解决您的问题:

      function makeRequest(ur,params) {
      
          return new Promise(function(resolve,reject) {
      
              fetch(url,params)
              .then((response) => {
      
                  let status = response.status;
      
                  if (status >= 200 && status < 300) {
                      response.json().then((data) => {
                          resolve(data);
                      })
                  }
                  else {
                      reject(response);
                  }
              })
          });
      }
      
      makeRequest().then(function success(data) {
         //...
      }, function error(response) {
          if (response.status === 401) {
              redirectToLoginPage();
          }
          else {
              response.json().then((error) => {
                  if (!error.message) {
                      error.message = constants.SERVER_ERROR;
                  }
      
                  //do sth. with error
              });
          } 
      });
      

      这意味着我会拒绝所有不良响应状态,然后在您的 error handlermakeRequest 中处理。

      【讨论】:

      • 你是否需要一个 .catch 处理程序来返回一个被拒绝的承诺?
      【解决方案5】:

      它可以工作并且不是真正的问题,除非makeRequest 的调用者期望实现承诺。所以,你在那里违反了合同。

      相反,您可以推迟承诺,或者(在这种情况下)使用状态码/错误拒绝。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-26
        • 2014-05-27
        • 2018-05-11
        • 2018-11-17
        • 1970-01-01
        • 2013-06-22
        • 1970-01-01
        • 2022-11-22
        相关资源
        最近更新 更多