# #reactjs #concurrency #firebase-storage #react-suspense
Вопрос:
Я пытаюсь использовать приостановку для загрузки модели GLTF из хранилища Firebase. Для этого мне нужно сначала асинхронно получить URL-адрес модели с помощью метода getDownloadURL, прежде чем я смогу ее загрузить. Я вижу, что загрузчик неоднократно вызывается, но ответ никогда не используется — я уверен, что пропустил что-то простое..
Я поместил код в эту песочницу кода, в ней используются примеры, которые кажутся довольно распространенными в Интернете, я заменил средство доступа firebase (так как для его работы потребуются закрытые ключи доступа по назначению), но функция замены довольно тривиальна, возвращая URL-адрес после тайм-аута.
Чтобы подвести итог песочнице, сердце, если это функция, чтобы получить URL-адрес загрузки, завернутый в функцию приостановки:
function getModelData(path) {
const storage = firebase.storage();
const urlPromise = storage.ref(path).getDownloadURL();
return { url: suspend(urlPromise) };
}
Это используется в моем коде следующим образом:
export default function Model(props) {
const modelData = getModelData(props.path);
const gltf = useGLTF(modelData.url.read());
return (
<mesh rotation={props.rotation} position={props.position} scale={props.scale}>
<primitive object={gltf.scene.clone(true)} dispose={null}/>
</mesh>
);
}
Функция приостановки правильно выдает свое обещание, и обещание разрешает результат настройки, но сама функция приостановки постоянно вызывается, а метод результата всегда не определен.
Ответ №1:
Это асинхронно getDownloadURL
, поэтому вам нужно будет написать свои функции следующим образом:
async function getModelData(path){
const storage = firebase.storage();
const urlPromise = await storage.ref(path).getDownloadURL();
return { url: suspend(urlPromise) };
}
Комментарии:
1. Спасибо, что нашли время ответить — я думаю, что смысл этого шаблона в том, что обещание, возвращаемое getModelData, передается в оболочку приостановки, чтобы его можно было дождаться, используя его метод then с приостановленным обещанием, возвращенным в стек. Я не верю, что асинхронность и ожидание здесь помогают.
2. Да, но вы не возвращаете это как Обещание. В этом-то и проблема. Вы возвращаете туда объект, в котором есть что-то внутри обещания.
Ответ №2:
Итак, я продолжил расследование и пришел к выводу, что это не сработает — проблема, с которой я сталкиваюсь, заключается в том, что я постоянно воссоздаю оболочку, а не создаю ее один раз и позволяю выполнить обещание и изменить статус приостановки. Я попытался переключиться на настройку состояния в крючке useEffect и загружать gltf только в том случае, если состояние было задано, но это выдало ошибку о несогласованном количестве крючков при рендеринге.
Вместо этого я использую состояние для установки URL-адреса модели и помещаю визуализированную модель во второй класс:
function ModelFromUrl(props) {
const gltf = useGLTF(props.url);
return (
<mesh rotation={props.rotation || [0, 0, 0]} position={props.position || [0, 0, 0]} scale={props.scale || [1, 1, 1]}>
<primitive object={gltf.scene.clone(true)} dispose={null}/>
</mesh>
);
}
export default function Model(props) {
const [url, setUrl] = useState();
useEffect(() => {
firebase.storage().ref(props.path).getDownloadURL().then(url => setUrl(url));
}, [props.path]);
if (! url) {
return null;
}
return <ModelFromUrl {...props} url={url}/>
}
Было бы неплохо заставить механизм ожидания работать, но, похоже, этого шаблона должно быть достаточно.