Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

143
Views
la función asíncrona actualiza el iterador correctamente al principio pero no al final

En el siguiente código, trabajo con CSS DOM, que puede ser un cálculo pesado. Aparentemente, esto es necesario para acceder al selector :after . El método renderItem agregará un nuevo elemento al DOM (incluido su elemento posterior) y esta es la razón por la que he usado la función async y await su devolución en cada iteración dentro de loadFromStorage .

Sin embargo, la await parece no funcionar correctamente, o sucede algo extraño dentro de la función renderItem . El iterador n se actualiza correctamente al comienzo de la función (los elementos se muestran correctamente en la pantalla y el primer console.debug imprime un valor correcto en el orden correcto), pero en la parte inferior, el segundo valor impreso es siempre el último valor de iteración (que es 4 en mi caso, ya que estoy tratando de representar 4 elementos del almacenamiento local) y el método getCSSRule obtiene un número incorrecto.

 let books = [] let n = 0 const renderItem = async (entry, direction = 1) => { const li = document.createElement('li') const ul = document.querySelector('ul') li.classList.add('item') n += 1 console.debug(`iter: ${n}`) li.id = (`item${n}`) await addCSSRule(`#item${n}:after`) li.innerText = entry.slice(0, entry.length - 13) if (direction === 1) ul.appendChild(li) else ul.insertBefore(li, ul.firstChild) console.debug(`iter: ${n}`) const s = await getCSSRule(`#item${n}::after`).catch(() => { console.debug(`Failed to find ':after' selector of 'item${n}'`) return false }) s.style.content = "\""+ entry.slice(entry.length - 13, entry.length) +"\"" return true } const loadFromStorage = () => { books = localStorage.getItem('books').split('//') books.forEach(async (entry) => { await renderItem(entry) }) } ...

Resultado de la consola (considerando localStorage.getItem('books').split('//') devuelve 4 elementos):

 iter: 1 iter: 2 iter: 3 iter: 4 iter: 4 // Printed x4

También he intentado pasar este método renderItem para await dentro de un objeto Promise , lo que me da el mismo resultado. Además, cuando actualizo el iterador n al final de la función, sucede lo mismo, pero al comienzo de la misma.

Lo siento si alguna terminología que he usado no es correcta en el contexto de JavaScript, no he estado usando este lenguaje durante muchos años y actualmente estoy tratando de entenderlo.

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

El problema clave aquí es que está pasando una función asíncrona a forEach , por lo que aunque esté await dentro del cuerpo, forEach no esperará la función en sí. Para ilustrar el orden de los eventos aquí, digamos que tiene 4 libros A, B, C, D. Su ejecución se verá así.

  • renderItem(A)
  • n += 1 (n ahora es 1)
  • console.log(n) (registros 1)
  • await addCSSRule(`#item${1}:after`) (Este es un evento realmente asíncrono, por lo que libera el bucle de eventos para que funcione en otras cosas, a saber, los siguientes elementos en forEach )
  • renderItem(B)
  • n += 1 (2)
  • console.log(n) (registros 2)
  • ...
  • renderItem(C) ... n += 1 (3) ... await addCSSRule
  • renderItem(D) ... n += 1 (4) ... await addCSSRule

Y luego, cada vez que se resuelvan las llamadas addCSSRule , n siempre será 4 , sin importar en qué llamada se encuentre.

Solución

Utilice un bucle for await...of en lugar de Array.prototype.forEach .

 for await (const entry of books) { await renderItem(entry); }

O un bucle for tradicional, y modifique renderItem para tomar n como argumento

 for (let i = 0; i < books.length; i++) { renderItem(books[i], i+1); // we don't need to await in this case, and we add 1 to i so that the 'n' value is 1-indexed to match your current behaviour. }

Preferiría la última opción, ya que es la mejor práctica para evitar el estado global mutable (su variable n ), ya que puede generar confusión en el flujo de control y problemas como el que está teniendo.

Otra opción es establecer una variable local en el valor de n después de incrementarla dentro de renderItem , de modo que durante la duración de esa función el valor no cambie, pero me parece una solución muy complicada.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!