React use为什么在Promise 是pending的时候还会render 一次?

我忘记在哪里看到的一个Suspense的实现机制里面会throw Promise,代码大概的逻辑是:

// use(promise) 的底层同步判断逻辑   伪代码
function use(promise) { 
  //给promise添加status属性.....
  const trackedPromise = trackPromiseStatus(promise);

  switch (trackedPromise.status) {
    case 'fulfilled':
      // 状态已完成?直接同步把数据拿出来返回!组件继续往下渲染!
      return trackedPromise.value; 
    case 'rejected':
      // 状态失败了?直接抛出错误,交给 ErrorBoundary 处理
      throw trackedPromise.reason; 
    case 'pending':
    default:
      // 还在 pending?直接把这个 promise throw 抛出去!
      // 中断当前组件的渲染,让外层的 Suspense 捕获它并显示 Fallback(Loading)
      throw trackedPromise; 
  }
}

注意上面如果是pending 或者未知 则会throw Promise

Demo代码 https://reurl.cc/GarLqG (或这通过地址2 打开 https://short-url.org/1lzVR)

Demo代码中 1. App 使用了非严格模式渲染, 2.PromiseA 延迟8秒resolve, PromiseB延迟11秒resolve

//非严格模式
root.render(React.createElement(App))

 const [promiseA] = useState(() => {
    console.log("create  PromiseA");
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve("promiseA");
      }, 8000);
    });
  });
  const [promiseB] = useState(() => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve("promiseB");
      }, 11000);
    });
  });

第一种情况 Suspense中只有一个Use Promise组件

 //app.jsx中 render一个 use promise 组件
 return (
    <div>
      <hr></hr>
      {/* 第一种情况 */}
      <Suspense fallback={<div>loading...</div>}>
        <ComponentC></ComponentC>
        <ReactUse promiseA={promiseA}></ReactUse>
      </Suspense>
      
    </div>
  );

Suspense内部包裹了一个use组件和一个正常的组件,他们在渲染的时候都会打印render日志.

最终的日志输出结果如下:

create  PromiseA
index.04db5034.js:738 render ComponentC
index.04db5034.js:738 render ReactUse component, PromiseA.status is undefined
index.04db5034.js:738 render ComponentC
index.04db5034.js:738 render ReactUse component, PromiseA.status is pending
index.04db5034.js:738 render ComponentC
index.04db5034.js:738 render ReactUse component, PromiseA.status is fulfilled

其中第7行有一个pending日志.

问题: 第一次渲染的时候 PromiseA 是一个普通的Promise对象 没有status属性,所以打印undefined, 在use内部 我们给他添加一个status=pending, 然后 throw出去. 既然throw出去了, 后需只需要等待这个promise fulfilled的时候再次渲染就可以了,为什么会有一个peding状态下的render? 是谁触发了这个render? 设计的逻辑是什么?

第二种情况 Suspense中有多个Use Promise组件

//app.jsx
 return (
    <div>
      <hr></hr>
      {/* 第二种情况 */}
      <Suspense fallback={<div>loading...</div>}>
        <ComponentC></ComponentC>
        <ReactUse promiseA={promiseA}></ReactUse>
        <ReactUseTwo promiseB={promiseB}></ReactUseTwo>
      </Suspense>
    </div>
  );
 1 create  PromiseA
 2 index.04db5034.js:738 render ComponentC
 3 index.04db5034.js:738 render ReactUse component, PromiseA.status is undefined
 4 index.04db5034.js:738 render ComponentC
 5 index.04db5034.js:738 render ReactUse component, PromiseA.status is pending
 6 index.04db5034.js:738 render ReactUsePromiseB component. PromiseB.status is undefined
 7 index.04db5034.js:738 render ComponentC
 8 index.04db5034.js:738 render ReactUse component, PromiseA.status is fulfilled
 9 index.04db5034.js:738 render ReactUsePromiseB component. PromiseB.status is pending
10 index.04db5034.js:738 render ComponentC
11 index.04db5034.js:738 render ReactUse component, PromiseA.status is fulfilled
12 index.04db5034.js:738 render ReactUsePromiseB component. PromiseB.status is fulfilled

从日志中可以看到
第3行为 ReactUse 组件的第一次render, promiseA.status 为undefined

第5行输出 promiseA pending状态的render, 这是ReactUse组件的第二次render

第 6行才输出ReactUsePromiseB 组件的第一次 render ,promiseB.status为undefined

第8行 日志是ReactUse组件的第三次渲染, promiseAstatus为fulfilled. 这说明 时间已经过去了8秒.

第9行 是ReactUsePromiseB的第二次渲染, status为pending. 这里我们看到 PromiseB的pending状态的render延迟到了 PromiseA fulfilled 之后. 但是 PromiseB的status undefined 的render 并没有在promiseA的fulfilled之后.

问题:

  1. 第9行 是ReactUsePromiseB的第二次渲染, status为pending. 这里我们看到 PromiseB的pending状态的render延迟到了 PromiseA fulfilled 之后. 但是 PromiseB的status undefined 的render 并没有在promiseA的fulfilled之后.
  2. 从 use的伪代码来看, 他内部只有在 fulfilled和reject的时候才会return, 其他时候都是throw promise. 从输出日志来看, 即便,PromiseA.status is pending 也没有阻止往下继续渲染 ReactUsePromiseB 组件(第6行日志. 6 index.04db5034.js:738 render ReactUsePromiseB component. PromiseB.status is undefined) 这内部的实现逻辑是怎样的?
阅读 515
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题