我忘记在哪里看到的一个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之后.
问题:
- 第9行 是ReactUsePromiseB的第二次渲染, status为pending. 这里我们看到 PromiseB的pending状态的render延迟到了 PromiseA fulfilled 之后. 但是 PromiseB的status undefined 的render 并没有在promiseA的fulfilled之后.
- 从 use的伪代码来看, 他内部只有在 fulfilled和reject的时候才会return, 其他时候都是throw promise. 从输出日志来看, 即便,PromiseA.status is pending 也没有阻止往下继续渲染 ReactUsePromiseB 组件(第6行日志. 6 index.04db5034.js:738 render ReactUsePromiseB component. PromiseB.status is undefined) 这内部的实现逻辑是怎样的?