异步的传染性问题 Asynchronous infectious issues [rawFetch-throw err]

20 阅读2分钟

解决方案

解决方案

解决代码示例

<!DOCTYPE html>
<html>
  <head>
    <title>异步的传染性问题</title>
    <script type="text/javascript">
      function A(){
        console.log('======= 异步的传染性问题 =======');
        async function getUser() { 
          return await fetch('./test.json').then(r=>r.json());
        }
        async function m1() { return await getUser(); }
        async function m2() { return await m1(); }
        async function m3() { return await m2(); }

        async function main() { 
          const user = await m3(); 
          console.log('[A] main(): user = ', user); 
        }
        main();
      }

      // A();
    </script>

    <script type="text/javascript">
      console.warn(window.fetch, '~~~~');
      function B(){
        console.log('======= 全部函数改为同步 =======');
        function getUser() { return fetch('./test.json'); }
        function m1() { const user = getUser(); return user; }
        function m2() { const user = m1(); return user; }
        function m3() { const user = m2(); return user; }
        function main() { const user = m3(); console.log('main execute -> user:', user); }

        function run(func) { 
          // 1. 保存旧的 fetch 
          const rawFetch = window.fetch;
          const cache = { status: 'pending', value: null };
          
          // 2. 重写 fetch 
          window.fetch = (...args) => {
            // 有缓存,交付缓存
            if (cache.status === 'fulfilled') return cache.value;
            if (cache.status === 'rejected') throw cache.value;

            const p = rawFetch(...args).then(r=>r.json())
              // .then() 方法接受两个参数,第一个是处理成功的回调,第二个是处理失败(错误)的回调
              .then(data => {
                cache.status = 'fulfilled'; // 成功时,将 cache.status 设置为 'fulfilled'
                cache.value = data;         // 将成功的返回值存储在 cache.value 中
              }, err => {
                cache.status = 'rejected';  // 失败时,将 cache.status 设置为 'rejected'
                cache.value = err;          // 将错误存储在 cache.value 中
                throw err;                  // 抛出错误,让外部能够捕获并处理
              });
            
            // 2. 抛出错误,中断执行 
            throw p; 
          }
          
          // 3. 执行函数 
          try { 
            // 执行 func() 即 main(), 此时 main 里的 fetch 是重写的 fetch.
            return func(); 
          } 
          catch (err) { 
            // 判断 err 是不是 throw p 返回的Promise类型, 是则执行 func();
            if (err instanceof Promise) {
              // 在 err 这个Promse 执行到最后时, 说明 getUser里的fetch 已经异步获取到了 json数据, 此时再次执行 func(); 获取数据;
              return err.then(()=>func());
            }

            // 非 Promise 的 error 也要抛出异步. 
            // throw err; 的作用是“我不处理这个异常,把它还给上层”,避免把别的错误误当成 Promise 情况而悄悄吃掉。
            throw err;
          } finally {
            // 4. 恢复 fetch 
            window.fetch = rawFetch;
          }
        }

        // main 会执行二次
        // 第一次: 执行fetch, 异步发出; main execute -> user: Promise(正在执行的异步请求)
        // 第二次: err.finally(()=>{ ...; func(); ... }) 获取到异步返回的数据. main execute -> user: 异步返回的json数据
        run(main);

        console.warn(window.fetch);
      }

      B();
    </script>
  </head>
  <body>

  </body>
</html>