useEffect(() => { const method = methodsToRun[0]; let results = []; if (method) { let paramsTypes = method[1].map(param => param[0][2]); let runAlgo = window.wasm.cwrap(method[0], 'string', paramsTypes); //this is emscripten stuff let params = method[1].map(param => document.getElementById(param[0][0]).value); let result = runAlgo(...params); //this runs the emscripten stuff results.push(...(JSON.parse(result))); setGlobalState('dataOutput', results); } let newMethodsToRun = methodsToRun.slice(1); if (methodsToRun.length>0) { setGlobalState('methodsToRun', newMethodsToRun); } } , [dataOutput, methodsToRun]);Hola, estoy trabajando en una aplicación ReactJS que usa Webassembly con Emscripten. Necesito ejecutar una serie de algoritmos de Emscripten en mi Js y necesito mostrar los resultados a medida que llegan, no del todo al final. Algo como esto:
Ya probé un bucle que actualiza un estado global en cada iteración con tiempos de espera, etc., pero no tuve suerte, así que probé este enfoque usando useEffect, probé varias cosas pero los resultados siguen apareciendo todos juntos y no uno por uno. ¿Cualquier pista?
Traté de burlarme de tu ejemplo AQUÍ :
import React, { useState, useEffect } from 'react'; import './style.css'; export default function App() { const [methodsToRun, setMethodsToRun] = useState([method1, method2]); const [globalState, setGlobalState] = useState([]); useEffect(() => { if (methodsToRun.length <= 0) return; const method = methodsToRun[0]; let results = []; if (method) { let paramsTypes = method[1].map((param) => param[0][2]); let runAlgo = window.wasm.cwrap(method[0], 'string', paramsTypes); //this is emscripten stuff let params = method[1].map( (param) => document.getElementById(param[0][0]).value ); let result = runAlgo(...params); //this runs the emscripten stuff results.push(...JSON.parse(result)); setGlobalState((global) => [...global, results]); } setMethodsToRun((methods) => methods.slice(1)); }, [methodsToRun]); console.log('METHODS TO RUN', methodsToRun); console.log('GLOBAL RESULT', globalState); return ( <div> <div> Global state 1: {globalState[0] && globalState[0][0]}</div> <div> Global state 2: {globalState[1] && globalState[1][0]}</div> <div id="1">DIV 1 </div> <div id="4">DIV 2</div> <div id="7">DIV 3</div> </div> ); } const method1 = [ '1param0', [ ['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9'], ], ]; const method2 = [ '2param0', [ ['11', '223', '3'], ['44', '55', '66'], ['77', '88', '99'], ], ]; //mock wasm window.wasm = { cwrap: (...args) => () => JSON.stringify(args), }; Este ejemplo asume que sus métodos wasm son sincrónicos, su DOM se actualizará y se volverá a representar con un nuevo globalState después de que se ejecute cada método, el ciclo está garantizado por el hecho de que está configurando una nueva matriz de métodos dividida por uno en el estado, que activará un rerender y una nueva ejecución de useEffect ya que tiene la matriz de métodos para ejecutar en la matriz methodsToRun . El bucle de procesamiento infinito se evita mediante la verificación if (methodsToRun.length <= 0) return; .
Sin embargo, el inconveniente de este enfoque es que si sus métodos wasms son sincrónicos y pesados, su interfaz de usuario se congelará hasta que regrese, y esa es una experiencia de usuario terrible para el usuario final. No estoy seguro de qué tipo de tarea está tratando de realizar allí, si se puede sincronizar y esperar (la lógica sería la misma) o si tiene que mover eso en un trabajador de servicio.
Entonces, por lo que ha explicado aquí, creo que el problema se puede resolver de 2 maneras:
1 es con solicitudes/procesos sincrónicos.
Suponiendo que tiene 2 funciones (AlgoX y AlgoY) que desea ejecutar una tras otra, puede devolver la respuesta esperada. p.ej
return AlgoX(); return AlgoY();La palabra clave de retorno bloqueará la continuación del proceso en ejecución hasta que obtenga un resultado.
Otra alternativa que sugeriré será usar Self-Invoking-Expression:
useEffect(() => { ( async function AlgoHandler(){ const method = methodsToRun[0]; let results = []; if (method) { let paramsTypes = method[1].map(param => param[0][2]); let runAlgo = await window.wasm.cwrap(method[0], 'string', paramsTypes); //this is emscripten stuff let params = method[1].map(param => document.getElementById(param[0][0]).value); let result = await runAlgo(...params); //this runs the emscripten stuff results.push(...(JSON.parse(result))); setGlobalState('dataOutput', results); } let newMethodsToRun = methodsToRun.slice(1); if (methodsToRun.length>0) { setGlobalState('methodsToRun', newMethodsToRun); } } )(); } , [dataOutput, methodsToRun]);Observe dónde usé async y await en la segunda solución. Déjame saber si es útil.
No estoy seguro de si esto responde a su pregunta, pero escribí un código de ejemplo que puede ayudarlo a satisfacer sus necesidades. Sin embargo, deberá modificarse para que se ajuste a su caso de uso, ya que esto debería considerarse un pseudocódigo:
const initialState = { count: 0, results: [], }; function reducer(state, action) { switch (action.type) { case 'addResult': return { ...state, count: state.count + 1, results: [...state.results, action.result], }; default: return state; } } function Example({ methodsToRun }) { const [{ count, results }, dispatch] = useReducer(reducer, initialState); const next = useCallback(async() => { if(methodsToRun.length > count) { const result = await methodsToRun[count](); dispatch({ type: 'addResult', result }); } }, [count, methodsToRun]); useEffect(() => { next(); }, [next]); return ( <div> <p>Results:</p> <ol> {results.map(result => ( <li key={result/* choose a unique key from result here, do not use index */}>{result}</li> ))} </ol> </div> ); }Básicamente, mi idea es crear una función de devolución de llamada, que en la primera ejecución será el método en el índice 0 en su conjunto de métodos. Cuando llegue el resultado, actualizará el estado con el resultado, así como también actualizará el contador.
El conteo actualizado hará que useCallback cambie al siguiente método en la matriz.
Cuando la devolución de llamada cambie, activará el efecto de uso y, de hecho, llamará a la función. Y así sucesivamente, hasta que no haya más métodos para ejecutar.
Tengo que admitir que no he intentado ejecutar este código, ya que me falta el contexto. Pero creo que debería funcionar.