Espera la entrada de usuario con Async / Await Syntax -- javascript campo con functional-programming campo con async-await campo con promise campo con ecmascript-8 camp codereview Relacionados El problema

await user input with async/await syntax


5
vote

problema

Español

para mayor claridad: se mudó aquí desde StackOverflow después de haber sido señalado a la revisión de código, sea el mejor lugar para esta pregunta

Amo a Async / Await y promete desde que me puse las manos. Y podría estar exagerándolo, pero se siente que debería haber una manera buena y legible de utilizar ASYNC / AWAIT para acercarse un poco más a un flujo como un estilo de programación funcional.

Me encantaría no tener que usar solo ASYNC / AWAIT para esperar a que los recursos web regresen, pero también para esperar la entrada del usuario cuando lo espere.

Hasta ahora, tengo algún código que funciona de manera similar a esta demostración acortada donde envuelve una sola vez solo en una promesa:

  std::list2  
  std::list3  
  std::list4  

Pero algo sobre este uso de los errores de Listener Listener. Además: uso una promesa que siempre se resuelve, lo que también parece extraño.

En el lado elevado: puedo leer el código de arriba a abajo y puede seguir el flujo de usuario dentro de la principal sin tener que perseguir eventos, devoluciones de llamada, etc.

Sin embargo, se siente como si estuviera reinventando algo que alguien más podría haber normado. Entonces:

¿Hay una mejor manera de lograr esto? ¿Existen mejores prácticas para trabajar con interacciones de usuario u otros eventos DOM en una forma ASYNC y / o testible?

Original en ingles

for clarity: moved here from stackoverflow after being pointed to Code Review being the better place for this question

I love async/await and Promises since I got my hands on them. And I might be overdoing it, but it feels like there should be a good and readable way to utilize async/await to get a little closer to a flow like, functionalISH programming style.

I would love to not have to only use async/await to wait for web resources to come back but also to wait for user input when I await it.

So far i have some code working similar to this shortened demo where I wrap a one time only EventListener into a Promise:

//// MAIN ///////  (async function(){      //...do some async await stuff here... fetching stuff from a server     // let services = await fetch(...) for example     let services = [{url:"x",label:"1"},{url:"y",label:"2"},{url:"z",label:"3"}]       let service_chosen = await showServicesToUserAndAwaitInput(services);      console.log("service_chosen:",service_chosen);     // ... go on.... })()  //// END MAIN /////   async function showServicesToUserAndAwaitInput(services){          if (services.length < 1){return null}      let choice = null;          let serviceList = document.querySelector("#serviceList");          // show list element     serviceList.classList.remove("hidden")       // create some elements for the user to interact with     for (let service of services){         let button = document.createElement("BUTTON");         button.innerHTML = service.label;         button.addEventListener("click",function(){           document.dispatchEvent(             new CustomEvent('serviceChosen', { detail:service })           )         });         serviceList.appendChild(button);     }          // returns promise with one time only event listener     return new Promise((resolve,reject)=>{          document.addEventListener("serviceChosen",function(e){             serviceList.classList.add("hidden") // hide again for we are done             resolve(e.detail)         },{ once: true })     }) }
.hidden{   visibility: hidden }
<div id="serviceList" class="hidden">  </div>

But something about this use of the EventListener bugs me. Also: I use a promise that always resolves, which also seems strange.

On the upside: I get to read the code top to bottom and can follow the user flow within the MAIN without having to chase down events, callbacks and so on.

Yet, it feels like I am reinventing something somebody else might have already normed. So:

Is there a better way to achieve this? Are there best practices to work with user interactions or other DOM events in a async and/or thenable way?

              
       
       

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 

Debe usar los observables y la innumerables funciones disponible para ellos. Se llama programación reactiva , pero creo que podría caer en la categoría de funcional ish .

un ejemplo de la memoria:

  import { fromEvent, takeUntil } from 'rxjs';  ...  const eventListener = document.addEventListener('serviceChosen', () => console.log('chosen'));  const unsubscribe = new Subject();  fromEvent(eventListener)     .pipe(takeUntil(unsubscribe))     .subscribe({         next: event => {             serviceList.classList.add('hidden');             unsubscribe.next();             unsubscribe.complete();         }     });   

Puede parecer un poco demasiado exagerado por una acción única, sin embargo, creo que es mejor que una promesa, y en general debe usar los observables sobre las promesas. Describiría la diferencia como las promesas que son escuchas "activas", ya que esperan completar, mientras que las suscripciones observables son más "pasivas", ya que nunca pueden ser despedidas y no esperan serlo, solo se convierten en fuego para disparar cuando son necesario.

 

You should use observables and the myriad of functions available for them. It's called reactive programming, but I think it might fall into the category of functionalish.

An example from memory:

import { fromEvent, takeUntil } from 'rxjs';  ...  const eventListener = document.addEventListener('serviceChosen', () => console.log('chosen'));  const unsubscribe = new Subject();  fromEvent(eventListener)     .pipe(takeUntil(unsubscribe))     .subscribe({         next: event => {             serviceList.classList.add('hidden');             unsubscribe.next();             unsubscribe.complete();         }     }); 

It may look a little bit overkill for a one-time action, however I think it's better than a promise, and in general you should use observables over promises. I would describe the difference as promises being "active" listeners as they expect to complete, while observable subscriptions are more "passive" since they may never get fired and don't expect to be, they're just setup to fire when they are needed.

 
 
         
         
4
 
vote

En primer lugar, no usaría promesas de hacer una programación basada en eventos. No se usa así, por lo que su código se pondrá más difícil de seguir.

Además, le aconsejaría que dividiera más tus funciones y omití algunos comentarios de esa manera.

  //// MAIN ///////  let services = [     { url: "x", label: "1" },     { url: "y", label: "2" },     { url: "z", label: "3" }, ];  showServicesToUser(services);  //// END MAIN /////  const serviceList = document.querySelector("#serviceList");  function createButtonFromService(service) {     let button = document.createElement("BUTTON");     button.innerHTML = service.label;     button.addEventListener("click", function () {         document.dispatchEvent(             new CustomEvent("serviceChosen", { detail: service })         );     });     serviceList.appendChild(button); }  function showServicesToUser(services) {     if (services.length < 1) return;      serviceList.classList.remove("hidden");      for (let service of services) {         createButtonFromService(service);     } }  function chooseService(service) {     console.log("service_chosen:", service);     // ... go on.... }  document.addEventListener("serviceChosen", function (e) {     serviceList.classList.add("hidden"); // hide again for we are done     chooseService(e.detail); }, { once: true });1  
  .hidden{   visibility: hidden }  
  <div id="serviceList" class="hidden">  </div>3  

Mi código propuesto anteriormente está escrito en un tipo de forma de guión, con una variable global ( 9988777665544334 ), pero también podría escribir todo esto en una clase (especialmente si tiene más código), Para que sea más legible y reutilizable.

  class ServiceChooser {     serviceListSelector;     serviceList;     services;      constructor(serviceListSelector, services) {         this.serviceListSelector = serviceListSelector;         this.services = services;          this.serviceList = document.querySelector(serviceListSelector);                  document.addEventListener(`serviceChosen${this.serviceListSelector}`, (e) => this.chooseService(e.detail), { once: true });     }      createButtonFromService(service) {         let button = document.createElement("BUTTON");          button.innerHTML = service.label;         button.addEventListener("click", () => {             const event = new CustomEvent(`serviceChosen${this.serviceListSelector}`, { detail: service });             document.dispatchEvent(event);         });          this.serviceList.appendChild(button);     }      chooseService(service) {         this.serviceList.classList.add("hidden");         console.log("service_chosen:", service);         // ... go on....     }          showServicesToUser() {         if (this.services.length < 1) return;          this.serviceList.classList.remove("hidden");          for (let service of this.services) {             this.createButtonFromService(service);         }     } }  //// MAIN ///////  let services = [     { url: "x", label: "1" },     { url: "y", label: "2" },     { url: "z", label: "3" }, ];  const sc = new ServiceChooser('#serviceList', services); sc.showServicesToUser();  // now you could add 2 service choosers, sperate from each other const sc2 = new ServiceChooser('#serviceList2', services); sc2.showServicesToUser();  //// END MAIN /////  
  .hidden{   visibility: hidden }  
  <div id="serviceList" class="hidden">  </div> <div id="serviceList2" class="hidden">  </div>  
 

First of all, I would not use Promises to do event based programming. It is not used like that, so your code will get harder to follow.

Also, I would advise you to split up your functions more and omit some comments that way.

//// MAIN ///////  let services = [     { url: "x", label: "1" },     { url: "y", label: "2" },     { url: "z", label: "3" }, ];  showServicesToUser(services);  //// END MAIN /////  const serviceList = document.querySelector("#serviceList");  function createButtonFromService(service) {     let button = document.createElement("BUTTON");     button.innerHTML = service.label;     button.addEventListener("click", function () {         document.dispatchEvent(             new CustomEvent("serviceChosen", { detail: service })         );     });     serviceList.appendChild(button); }  function showServicesToUser(services) {     if (services.length < 1) return;      serviceList.classList.remove("hidden");      for (let service of services) {         createButtonFromService(service);     } }  function chooseService(service) {     console.log("service_chosen:", service);     // ... go on.... }  document.addEventListener("serviceChosen", function (e) {     serviceList.classList.add("hidden"); // hide again for we are done     chooseService(e.detail); }, { once: true });
.hidden{   visibility: hidden }
<div id="serviceList" class="hidden">  </div>

My proposed code above is written in a scripted kind of way, with a global variable (serviceList), but you could also write this all in a class (especially if you have more code), to make it more readable and reusable.

class ServiceChooser {     serviceListSelector;     serviceList;     services;      constructor(serviceListSelector, services) {         this.serviceListSelector = serviceListSelector;         this.services = services;          this.serviceList = document.querySelector(serviceListSelector);                  document.addEventListener(`serviceChosen${this.serviceListSelector}`, (e) => this.chooseService(e.detail), { once: true });     }      createButtonFromService(service) {         let button = document.createElement("BUTTON");          button.innerHTML = service.label;         button.addEventListener("click", () => {             const event = new CustomEvent(`serviceChosen${this.serviceListSelector}`, { detail: service });             document.dispatchEvent(event);         });          this.serviceList.appendChild(button);     }      chooseService(service) {         this.serviceList.classList.add("hidden");         console.log("service_chosen:", service);         // ... go on....     }          showServicesToUser() {         if (this.services.length < 1) return;          this.serviceList.classList.remove("hidden");          for (let service of this.services) {             this.createButtonFromService(service);         }     } }  //// MAIN ///////  let services = [     { url: "x", label: "1" },     { url: "y", label: "2" },     { url: "z", label: "3" }, ];  const sc = new ServiceChooser('#serviceList', services); sc.showServicesToUser();  // now you could add 2 service choosers, sperate from each other const sc2 = new ServiceChooser('#serviceList2', services); sc2.showServicesToUser();  //// END MAIN /////
.hidden{   visibility: hidden }
<div id="serviceList" class="hidden">  </div> <div id="serviceList2" class="hidden">  </div>
 
 
   
   
3
 
vote

Estoy de acuerdo en que no tiene sentido usar las promesas con la programación basada en eventos y el uso de observables es una buena solución.

En otra nota, se recomienda predeterminarse para usar const en lugar de let para todas las variables como puede causa errores . Cuando determine la asignación de reesignación (principalmente para variables de bucle / iterador), use //// MAIN /////// let services = [ { url: "x", label: "1" }, { url: "y", label: "2" }, { url: "z", label: "3" }, ]; showServicesToUser(services); //// END MAIN ///// const serviceList = document.querySelector("#serviceList"); function createButtonFromService(service) { let button = document.createElement("BUTTON"); button.innerHTML = service.label; button.addEventListener("click", function () { document.dispatchEvent( new CustomEvent("serviceChosen", { detail: service }) ); }); serviceList.appendChild(button); } function showServicesToUser(services) { if (services.length < 1) return; serviceList.classList.remove("hidden"); for (let service of services) { createButtonFromService(service); } } function chooseService(service) { console.log("service_chosen:", service); // ... go on.... } document.addEventListener("serviceChosen", function (e) { serviceList.classList.add("hidden"); // hide again for we are done chooseService(e.detail); }, { once: true });0 .

Otra sugerencia es usar un LINTER-E.G. eslint , jslint < / a>, etc., por ejemplo: las líneas como esta serían atrapadas por la regla de la Eslint Espacio de llaves :

  //// MAIN ///////  let services = [     { url: "x", label: "1" },     { url: "y", label: "2" },     { url: "z", label: "3" }, ];  showServicesToUser(services);  //// END MAIN /////  const serviceList = document.querySelector("#serviceList");  function createButtonFromService(service) {     let button = document.createElement("BUTTON");     button.innerHTML = service.label;     button.addEventListener("click", function () {         document.dispatchEvent(             new CustomEvent("serviceChosen", { detail: service })         );     });     serviceList.appendChild(button); }  function showServicesToUser(services) {     if (services.length < 1) return;      serviceList.classList.remove("hidden");      for (let service of services) {         createButtonFromService(service);     } }  function chooseService(service) {     console.log("service_chosen:", service);     // ... go on.... }  document.addEventListener("serviceChosen", function (e) {     serviceList.classList.add("hidden"); // hide again for we are done     chooseService(e.detail); }, { once: true });1  
 

I agree it doesn't make sense to use promises with event-based programming and using observables is a good solution.

On another note, it is recommended to default to using const instead of let for all variables as it can cause bugs. When you determine re-assignment is necessary (mostly for loop/iterator variables) then use let.

Another suggestion is to use a linter - e.g. ESLint, JSLint, etc. For example: lines like this would be caught for the ESLint rule key-spacing:

new CustomEvent('serviceChosen', { detail:service }) 
 
 

Relacionados problema

6  Nodejs Crawler para recetas  ( Nodejs crawler for recipes ) 
Aquí está mi intento en un rastreador hecho en nodejs con cheerio , lo hice con la idea en Mente para usarlo en un futuro proyecto que quiero hacer. Aquí est...

5  Espera la entrada de usuario con Async / Await Syntax  ( Await user input with async await syntax ) 
para mayor claridad: se mudó aquí desde StackOverflow después de haber sido señalado a la revisión de código, sea el mejor lugar para esta pregunta Amo a ...

9  Método para delegar comando a otros métodos  ( Method to delegate command to other methods ) 
Dado que estoy trabajando solo en esto, me gustaría alguien que echaría un vistazo y señalar cualquier error o cosas que podría haber hecho mejor. Mis pregu...

4  Acceso a la regeneración de token para solicitudes de Axios  ( Access token regeneration for axios requests ) 
Descripción He desarrollado una aplicación que usa axios para comunicarse con el PayPal API . PayPal tiene una Nodejs SDK , pero desafortunadamente ...

2  Emulionar el operador de la tubería en JavaScript de una manera legible  ( Emulating the pipe operator in javascript in a readable way ) 
en este artículo Encontré este código de elixir de aspecto bastante elegante: < / p> conn |> put_session(:current_user, user) |> put_resp_header(location,...

4  Herramienta node.js para actualizar Cisco UCCX con los datos de CSV  ( Node js tool to update cisco uccx with csv data ) 
Qué esta herramienta actualiza habilidades para agentes en un clúster de centro de contacto (Cisco UCCX). La herramienta lee un archivo CSV llamado Agents.csv...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos