如果您不解决或拒绝诺言,会发生什么?


73

我有一个我要兑现诺言的情况。承诺基本上是由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,则我将重定向到登录页面。诺言既没有解决也没有被拒绝。

这段代码可以吗,还是有更好的方法来做到这一点?

Answers:


114

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

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

您可能还有其他一些代码可以完成该任务的工作,而诺言只是在没有做任何事情的情况下就被放弃了。如果这样做的话,Javascript不会出现内部问题,但是,这不是承诺的设计方式,通常也不是承诺的使用者期望它们如何运作。

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

这个代码可以吗?还是有更好的方法来做到这一点。

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


4
如您所述,重定向到新的浏览器页面将完全清除当前页面状态(包括所有Javascript状态)。但是,如果我在单页面应用程序中重定向,将会发生什么。我正在使用ReactJS。因此,将没有新的浏览器页面,只是一个不同的视图。在这种情况下,通过重定向使用快捷方式并保留其他未解决的内容是否完美?
Aniket '16

@Aniket-那是另一种情况,您的问题没有描述。您将必须确保正确清理所有内容,以免发生内存泄漏。我不知道这种反应是否存在问题。下次,请在您的问题中输入此类信息。
jfriend00 '16

确保您没有对rejectandresolve函数的引用,而不仅仅是停止引用promise。然后,GC应该介入。否则,如果您的代码仍然引用resolve例如,那么Promise将不得不坚持下去,以防您的代码调用resolve()。这是一个Web API的示例,如果不小心,它会泄漏Promises:github.com/w3c/webcomponents/issues/674
trusktr

对于您的单页面应用程序场景,当处理401时,您可以按照下面的@CodingIntrigue所述“返回”。当然,您需要if(typeof json ===“ undefined”)检查,但是makeRequest的调用者应该检查它们是否仍然具有良好的数据。呼叫者永远不要盲目地相信自己拥有良好的数据。对于401,我不会拒绝诺言。对我的承诺被拒绝意味着应该记录一个错误……这实际上不是错误,而是简单的重定向。
RayLoveless

6

我认为“如果我们不解决拒绝问题会发生什么”已经很好地回答了-您可以选择添加a.then或a .catch

但是,此代码可以吗?还是有更好的方法来做到这一点。我会说两件事:

您在new Promise没有必要的情况下包装了Promise,并且fetch调用可能失败,您应该对此采取行动,以使您的调用方法不会坐下来等待永远无法解决的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处理程序将永远不会执行。


1
我只是想避免if(typeof json ===“ undefined”){//发生重定向请求}原因makeRequest被许多其他函数调用,因此我必须在调用它的每个函数中进行检查。
Aniket '16

我不确定我是否在遵循您的代码示例。因此,对于两个示例,makeRequest(..)。catch都不会被调用?是对的吗?那就是它对我的解读。似乎您需要在原始的访存调用中捕获并让其返回被拒绝的承诺。
RayLoveless

2

正如其他人所说,如果您不解决/拒绝诺言,那的确不是问题。无论如何,我会解决您的问题有点不同:

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 handler由您处理makeRequest


您是否需要调用.catch处理程序来返回拒绝的诺言?
RayLoveless

1

它可以工作,并且不是真正的问题,除非makeRequest期望的诺言者实现了。因此,您正在违反合同。

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


0

ECMAScript规范说明了promise的目的和new Promise()

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

25.6.3.1 Promise(执行者

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

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

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

因此,建议不要new Promise()像普通JavaScript程序一样编写此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 });
  }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.