如何使第二个请求等待上一个请求并使用其数据?

时间:2019-07-02 11:32:30

标签: reactjs

有2个React组件,两个组件都将请求发送到服务器(带有componentDidMount)。每个请求都会查看令牌是否过旧;如果是,则使用refreshToken对其进行更新。

问题-第一个请求将成功更新令牌,将返回新令牌和新的refreshToken。第二个请求将使用旧的refreshToken发送到服务器,服务器将生成错误。

解决方案是声明一个全局变量(window.pending)。如果为假-我们不更新令牌,如果令牌已更新-为它分配一个承诺,等待其完成并应用生成的令牌。

// From component 1

loadClient = async () => {
  const token = await this.props.getToken();
  // other code
};

// From component 2

loadProject = async () => {
  const token = await this.props.getToken();
  // other code
};

// НОС

const tokenProviderHOC = WrappedComponent =>
  class extends PureComponent<Props> {
    request = null;

    componentWillUnmount() {
      if (this.request !== null) this.request.abort(); //Cancel server request
    }

    checkToken = userId => async () => {
      const { history } = this.props;

      // token, refreshToken, expiresIn from localStorage
      const token = methods.getToken("token");
      const refreshToken = methods.getRefreshToken("refreshToken");
      const expiresIn = methods.getExpiresIn("expiresIn");

      if (Date.now() >= expiresIn) {
        if (window.pending === false) {
          console.log("we are first");

          let res;

          this.request = this._updateToken({ refreshToken, userId });

          window.pending = new Promise(async resolve => {
            console.log("request start");

            res = await this.request.ready(); //ready - start server request
            window.pending = false;

            console.log("request end", res ? res.token : false);
            return res ? resolve(res.token) : resolve(false);
          });
        }
        console.log("Token is already looking");
        return window.pending;
      }
      return token;
    };

    render() {
      const { userId, history, ...props } = this.props;

      return <WrappedComponent {...props} getToken={this.checkToken(userId)} />;
    }
  };

const mapStateToProps = state => ({
  userId: userSelectors.getUserInfo(state)?.id || null,
});

export const tokenProvider = compose(
  withRouter,
  connect(mapStateToProps),
  tokenProviderHOC,
);

必须显示在控制台中:

  • “我们是第一位”
  • “请求开始”或“令牌已在查找”
  • “请求结束”

现在控制台显示:

  • “我们是第一位”
  • “请求开始”
  • “令牌已经在寻找”
  • “请求结束”
  • “我们是第一位”
  • “请求开始”
  • “令牌已经在寻找”
  • tokenProvider.js:121 PUT http://site.loc/user/1/send net :: ERR_ABORTED 400(错误请求)
    //因为令牌是另一个
  • 请求结束为假”

似乎第二个请求正在等待第一个请求,然后再次尝试从传递旧的refreshToken的服务器中查找令牌。如何解决问题?

1 个答案:

答案 0 :(得分:0)

忽略HOC内容,因为在这里并不重要,类似的事情应该起作用。这样的想法是,您有一个承诺,该承诺将最终解析为一个新令牌。

此外,您也可以使用(模块)全局变量,除非特别需要,否则无需将任何内容存储在window中。

tokenIsValid_updateToken应该或多或少是不言自明的,并且这里省略了错误处理。

let fetchTokenPromise = null;
let currentToken = null;

function getToken() {
  if (currentToken && tokenIsValid(currentToken)) {
    // Current token is valid, return it (as a promise).
    return Promise.resolve(currentToken);
  }

  if (!fetchTokenPromise) {
    // We're not fetching a token, start fetching one.
    fetchTokenPromise = new Promise(async (resolve, reject) => {
      const token = await _updateToken(currentToken);
      currentToken = token; // Update saved token.
      fetchTokenPromise = null; // Clear fetching state.
      resolve(token);
    });
  }
  return fetchTokenPromise;
}

// ...

const token = await getToken();  // this will either get the current token, or start refreshing one