目录
  • 前言
  • 代码案例
  • CancelablePromise (取消Promise)
  • 使用
    • 多个Promise链式调用
    • 在async和await中使用
  • 结束语

    前言

    最近项目当中小伙伴遇到一个很奇怪的bug,进入一个页面后,快速切换到其它页面,会跳转到403页面。经过一段时间和小伙伴的排查,发现那个页面有个接口请求响应时间比较长,请求后还有一些业务处理。

    等我们切换到其它页面,这个请求完成后还会处理剩下的业务,导致出错。

    代码案例

    项目当中有很多业务,我们用一些简单代码复现下这个问题。

    import React, { useEffect } from 'react';
    import { history } from 'umi';
    const Test = props => {
      useEffect(() => {
        new Promise((resolve, reject) => {
            // 模拟接口请求时间
            setTimeout(() => {
                resolve()
            }, 4000);
        }).then(res => {
            return new Promise((resolve1) => {
                 // 模拟接口请求时间
                setTimeout(() => {
                    resolve1()
                }, 1000);
            })
        }).then(() => {
            // Promise 执行完后页面跳转
            history.push('/test1')
        })
      }, []);
      const go = () => {
        history.push('/user/login')
      }
      return (
        <div>
          <button onClick={go}>go to</button>
          Test
        </div>
      );
    }
    export default Test;
    

    我们进入Test组件后,马上点击go to按钮,几秒之后页面还会跳转到test1页面。

    经分析,我们应该在离开的时候要取消请求和取消Promise让后续的业务代码不在执行,取消请求比较简单,一般的库都支持,我们来说下怎么取消Promise.

    CancelablePromise (取消Promise)

    我们知道Promise是没有提供取消或者终止的操作。但我们在开发过程中会遇到。我们可以参考和借助AbortController来实现。

    class CancelablePromise<T> {
      /**
       * 构造器
       * @param executor Promise中的 executor
       * @param abortSignal AbortController中的signal对象
       * @returns 
       */
      constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void, abortSignal: AbortSignal) {
        // 记录reject和resolve方法
        let _reject: any = null;
        let _resolve: any = null;
        let _isExecResolve = false;
        // 创建和执行Promise
        const cancelablePromise = new Promise<T>((resolve, reject) => {
          _reject = reject;
          _resolve = (value: T) => {
            _isExecResolve = true;
            resolve(value);
          };
          return executor(_resolve, reject);
        });
        // 监听Signal的abourt事件
        abortSignal.addEventListener('abort', () => {
          if (_isExecResolve) {
            return;
          }
          // 抛出错误
          const error = new DOMException('user cancel promise', CancelablePromise.CancelExceptionName );
          _reject( error );
        } );
        return cancelablePromise;
      }
      // 取消后抛出的异常名称
      static CancelExceptionName = 'CancelablePromise AbortError';
    }
    export default CancelablePromise;
    

    使用

    下面我们该造下之前的代码,用我们封装的CancelablePromise,可以让Promise可取消。

    多个Promise链式调用

    import React, { useEffect } from 'react';
    import { history } from 'umi';
    import CancelablePromise from '../utils/CancelablePromise';
    const Test = props => {
      useEffect(() => {
       let abortController = new AbortController();
        new CancelablePromise((resolve, reject) => {
            // 模拟接口请求时间
            setTimeout(() => {
                resolve()
            }, 4000);
        }, abortController.signal).then(res => {
            return new CancelablePromise((resolve1) => {
                 // 模拟接口请求时间
                setTimeout(() => {
                    resolve1()
                }, 1000);
            }, abortController.signal)
        }).then(() => {
            // Promise 执行完后页面跳转
            history.push('/test1')
        })
        return () => {
            // 取消请求
            abortController.abort();
        }
      }, []);
      const go = () => {
        history.push('/user/login')
      }
      return (
        <div>
          <button onClick={go}>go to</button>
          Test
        </div>
      );
    }
    export default Test;
    

    在async和await中使用

    import React, { useEffect } from 'react';
    import { history } from 'umi';
    import CancelablePromise from '../utils/CancelablePromise';
    const Test = props => {
      useEffect(() => {
          let abortController = new AbortController();
         const exec = async function () {
          try {
            await new CancelablePromise((resolve) => {
              setTimeout(() => {resolve();}, 5000);
            }, abortController.signal, 'one')
            await new CancelablePromise((resolve1) => {
              setTimeout(() => {resolve1();}, 5000);
            }, abortController.signal, 'del')
            history.push('/test')
          } catch (error) {
            // 取消之后会抛出异常
            if (CancelablePromise.CancelExceptionName === error.name) {
              console.log('promise 终止了。。。')
            }
          }
        }
        exec();
        return () => {
            // 取消请求
            abortController.abort();
        }
      }, []);
    }
    

    Promise取消之后会抛出异常,如果需要在抛出异常之后做处理,可以通关对应的异常名称做判断。

    if (CancelablePromise.CancelExceptionName === error.name) {
      console.log('promise 终止了。。。')
    }
    

    结束语

    Promise的取消在业务当中用到的地方比较多,更多关于Promise运行取消的资料请关注其它相关文章!

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。