如果您不解决或拒绝承诺会怎样?

What happens if you don't resolve or reject a 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,我将重定向到登录页面。承诺既未解决也未拒绝。

这段代码可以吗,或者有更好的方法来实现吗?

它有效并且不是真正的问题,除非 makeRequest 的调用者期望实现承诺。所以,你在那里违约了。

相反,您可以推迟承诺,或(在本例中)拒绝状态 code/error。

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

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

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

As you can see if the status is 401, I am redirecting to login page. Promise is neither resolved nor rejected.

Is this code OK? Or is there any better way to accomplish this.

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

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

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
        });
    } 
});

这意味着我会拒绝每一个不良响应状态,然后在您的 makeRequesterror handler 中处理它。

我认为 "what happens if we don't resolve reject" 的回答很好 - 您可以选择添加 .then 还是 .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 处理程序将永远不会执行。

ECMAScript 规范解释了 promises 和 new Promise() 的目的:

A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.

25.6.3.1 Promise ( executor )

NOTE The executor argument must be a function object. It is called for initiating and reporting completion of the possibly deferred action represented by this Promise object.

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

由于您还将程序控制流(重定向逻辑)混合到您的 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 });
  }
}