react Suspense组件异步处理原理

1,291 阅读1分钟

react内置的Suspense组件有一个“新颖”的功能就是可以用同步的写法来写异步代码。例:

function ProfileDetails() {
    const user = resource.user.read(); // 这一步包含异步从服务端获取数据

    return <h1>{user.name}</h1>;
}

上述代码在不用Suspense组件时,一般情况下会认为应该这么写:

async function ProfileDetails() {
    const user = await resource.user.read(); 
    
    return <h1>{user.name}</h1>;
}

但react用一种独特的方式绕过了async,await语法,下面我来模拟实现下:

首先写一个异步函数:

const fetchData = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('我是数据')
        }, 1000);
    });
}

在写一个wrap函数,用于将普通的异步函数包装为Suspense需要用到的格式。

const wrap = (prms) => {
    let status = 'pending';
    let result;
    prms.then((r) => {
        result = r;
        status = 'success';
    },
    (err) => {
        result = err;
        status = 'error'
    });

    return () => {
        if (status === 'pending') {
            throw prms;    // 此处为重点
        }
        if (status === 'error') {
            throw result;
        }
        return result;
    }
}

先写一个组件,此组件是我们最终想达到的效果:

const tryReadData = wrap(fetchData()); // 获取包装后的函数

const reactComponent = () => {
    const data = tryReadData(); // 需要满足不写await,但需要获取到最终数据
    return <div>{data}</div>
}

具体实现:

try {
    reactComponent()
} catch(err) {
    if (err instanceof Promise) {
         // 如果捕获到的错误是个Promise类型,则说明异步还未完成
         // 等待异步完成在重新执行组件函数。
        err.then(reactComponent)
    } else if (err instanceof Error) {
        throw err;
    } else {
        ...
    }
}