una termine terminar que promesas pagina node funcion esperar ejecutar ejecucion controlar cargar asincronia asincrona antes anidadas javascript ajax asynchronous async-await synchronize

termine - promesas javascript



¿Cómo esperar una llamada asíncrona en JavaScript en una función síncrona? (3)

Recientemente tuve que corregir problemas de seguridad en una aplicación web (que no creé). El problema de seguridad era que usaba cookies que no eran solo de http. Así que tuve que configurar la cookie de sesión http-only, lo que significa que ya no puede leer (y establecer) el valor de la cookie desde javascript. Hasta ahora tan seamingly fácil.

El problema más profundo era, la aplicación web utilizada

JSON.parse(readCookie(cookieName)).some_value

en un millón de lugares .

Entonces, para no tener que volver a escribir "un millón de líneas de código", tuve que crear un punto final ajax que me proporcionara el contenido de la cookie http como JSON y reescribí readCookie para usar solicitudes ajax SINCRÓNICAS (en lugar de leer la cookie) ), porque el resto del horrible código espera que readCookie sea sincrónico en estos millones de lugares, porque leer una cookie es sincrónico.

El problema ahora es que tengo un montón de

La XMLHttpRequest sincrónica en el hilo principal está en desuso debido a sus efectos perjudiciales para la experiencia del usuario final. Para obtener más ayuda, consulte https://xhr.spec.whatwg.org/ .

que espacia la consola de depuración, y mucho menos la posibilidad de que alguien decida eliminar esta funcionalidad.

Por lo tanto, estoy investigando las nuevas palabras clave ES async / await, para ver si eso podría ayudar de alguna manera a realizar una solicitud ajax asíncrona de forma síncrona (sé que tengo que usar wrappers para IE 11).

Hasta ahora, leo estas páginas
https://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html
https://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html
https://jakearchibald.com/2014/es7-async-functions/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function *

pero parece que todas las nuevas cosas asincrónicas parecen responder más fácilmente al problema de escribir código asíncrono más fácil, no permitiendo la interoperabilidad entre el código síncrono asincrónico y el existente. Usando la información que leo, ahora puedo esperar el resultado de una llamada ajax asíncrona como si fuera sincrónica, pero el problema es que - aguardar solo está permitido en métodos asíncronos ... Lo que significa que incluso si puedo esperar el resultado como este fue sincrónico, el método getCookie todavía tendría que ser asincrónico, lo que hace que todas las cosas parezcan completamente inútiles (a menos que todo tu código sea asincrónico, lo que ciertamente no ocurre cuando no comienzas desde cero). .

Parece que no puedo encontrar ninguna información sobre cómo interoperar entre el código síncrono y el asíncrono.

Por ejemplo, en C #, puedo llamar a un método async desde un contexto síncrono con .Result, por ej.

AsyncContext.RunTask(MyAsyncMethod).Result;

o más fácil pero menos seguro como un punto muerto

MyAsyncMethod(args).Result;

¿Hay alguna forma de lograr lo mismo en JavaScript?

Parece tener poco sentido difundir la sincronización, cuando el resto de la base de código es sincrónico, sin posibilidad de intervención ... ¿Todavía no hay forma de lograr esto en JavaScript en 2017 AD?

Enfatizo nuevamente :
cómo puedo hacer una llamada ajax sincrónica, y cómo usar llamadas async ajax con devoluciones de llamada y / o promesas.
Pero lo que no puedo descifrar es cómo sincronizar una llamada async-ajax (sin devolución de llamada) para que pueda usarse desde un código que espera ejecutarse sincrónicamente (en "un millón de lugares").

Esto es lo que he intentado hasta ahora:
(Tenga en cuenta que si utilizo loadQuote o main , el texto "Ron dijo una vez" todavía aparece primero en la consola de depuración, que no debería ser el caso si la llamada ajax asíncrona se hubiera resuelto de forma síncrona )

<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="cache-control" content="max-age=0" /> <meta http-equiv="cache-control" content="no-cache" /> <meta http-equiv="expires" content="0" /> <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" /> <meta http-equiv="pragma" content="no-cache" /> <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="google" value="notranslate" /> <!-- <meta name="author" content="name" /> <meta name="description" content="description here" /> <meta name="keywords" content="keywords,here" /> <link rel="shortcut icon" href="favicon.ico" type="image/vnd.microsoft.icon" /> <link rel="stylesheet" href="stylesheet.css" type="text/css" /> --> <title>Title</title> <style type="text/css" media="all"> body { background-color: #0c70b4; color: #546775; font: normal 400 18px "PT Sans", sans-serif; -webkit-font-smoothing: antialiased; } </style> <script type="text/javascript"> <!-- // http://localhost:57566/foobar/ajax/json.ashx var ajax = {}; ajax.x = function () { if (typeof XMLHttpRequest !== ''undefined'') { return new XMLHttpRequest(); } var versions = [ "MSXML2.XmlHttp.6.0", "MSXML2.XmlHttp.5.0", "MSXML2.XmlHttp.4.0", "MSXML2.XmlHttp.3.0", "MSXML2.XmlHttp.2.0", "Microsoft.XmlHttp" ]; var xhr; for (var i = 0; i < versions.length; i++) { try { xhr = new ActiveXObject(versions[i]); break; } catch (e) { } } return xhr; }; ajax.send = function (url, callback, method, data, async) { if (async === undefined) { async = true; } var x = ajax.x(); x.open(method, url, async); x.onreadystatechange = function () { if (x.readyState == 4) { callback(x.responseText) } }; if (method == ''POST'') { x.setRequestHeader(''Content-type'', ''application/x-www-form-urlencoded''); } x.send(data) }; ajax.get = function (url, data, callback, async) { var query = []; for (var key in data) { query.push(encodeURIComponent(key) + ''='' + encodeURIComponent(data[key])); } ajax.send(url + (query.length ? ''?'' + query.join(''&'') : ''''), callback, ''GET'', null, async) }; ajax.post = function (url, data, callback, async) { var query = []; for (var key in data) { query.push(encodeURIComponent(key) + ''='' + encodeURIComponent(data[key])); } ajax.send(url, callback, ''POST'', query.join(''&''), async) }; /////////// function testAjaxCall() { ajax.get("./ajax/json.ashx", null, function (bError, strMessage, iStatus) { console.log("args:", arguments); console.log("Error:", bError); console.log("Message:", strMessage); console.log("Status:", iStatus); } , true ); } --> </script> </head> <body> <script type="text/javascript"> function getQuote() { var quote; return new Promise(function (resolve, reject) { ajax.get("./ajax/json.ashx", null, function (bError, strMessage, iStatus) { // console.log("args:", arguments); // console.log("Error:", bError); // console.log("Message:", strMessage); // console.log("Status:", iStatus); quote = bError; resolve(quote) }, true); /* request(''./ajax/json.ashx'', function (error, response, body) { quote = body; resolve(quote); }); */ }); } async function main() { var quote = await getQuote(); console.log("quote: ", quote); } function myGetQuote() { var quote = async function () { return await getQuote(); }; console.log("quote: ", quote); return quote; } function spawn(generatorFunc) { function continuer(verb, arg) { var result; try { result = generator[verb](arg); } catch (err) { return Promise.reject(err); } if (result.done) { return result.value; } else { return Promise.resolve(result.value).then(onFulfilled, onRejected); } } var generator = generatorFunc(); var onFulfilled = continuer.bind(continuer, "next"); var onRejected = continuer.bind(continuer, "throw"); return onFulfilled(); } function loadQuote() { return spawn(function *() { try { let story = yield getQuote(); console.log("story:", story); // addHtmlToPage(story.heading); // for (let chapter of story.chapterURLs.map(getJSON)) { addHtmlToPage((yield chapter).html); } addTextToPage("All done"); } catch (err) { //addTextToPage("Argh, broken: " + err.message); console.log("Argh, broken: " + err.message); } //document.querySelector(''.spinner'').style.display = ''none''; }); } function autorun() { console.clear(); // main(); // main(); loadQuote(); //var quote = myGetQuote(); // console.log("quote: ", quote); console.log(''Ron once said,''); } if (document.addEventListener) document.addEventListener("DOMContentLoaded", autorun, false); else if (document.attachEvent) document.attachEvent("onreadystatechange", autorun); else window.onload = autorun; </script> </body> </html>


pero el problema es que la espera solo está permitida en los métodos asíncronos.

Exactamente, y no, no hay solución para eso. La semántica de ejecución de JavaScript requiere que las funciones sincrónicas se completen antes de que se ejecute cualquier acción asincrónica pendiente (como la devolución de llamada a un manejador XHR para una llamada asincrónica XHR).

La forma en que JavaScript se ejecuta en un hilo determinado es que procesa una cola de trabajos 1 :

  1. Recoge el siguiente trabajo pendiente
  2. Ejecute sincrónicamente el código para ese trabajo
  3. Solo cuando ese trabajo finalice regrese al Paso 1 para recoger el siguiente trabajo

(Es un poco más complicado que eso, hay dos niveles, pero eso no es relevante para esta pregunta en particular).

Las terminaciones de XHR y esas son tareas que se programan en la cola. No hay forma de pausar un trabajo, ejecutar otro trabajo desde la cola y recoger el trabajo detenido. async / await proporciona una sintaxis dramáticamente más simple para manejar operaciones asíncronas, pero no cambian la naturaleza de la cola de trabajos.

La única solución que veo para tu situación es sincronizar todo el camino hasta el nivel superior. Esto puede no ser tan complicado como podrías pensar (o tal vez sea). En muchos casos, agrega async al frente de la function en muchas funciones. Sin embargo, hacer que esas funciones sean asíncronas es probable que tenga efectos de arrastre significativos (por ejemplo, algo que fue sincrónico en un controlador de eventos que se vuelve asíncrono cambia el tiempo de lo que sucede en relación con la IU).

Por ejemplo, considere este código síncrono:

var btn = document.getElementById("btn"); btn.addEventListener("click", handler, false); function handler(e) { console.log("handler triggered"); doSomething(); console.log("handler done"); } function doSomething() { doThis(); doThat(); doTheOther(); } function doThis() { console.log("doThis - start & end"); } function doThat() { console.log("doThat - start"); // do something that takes a while var stop = Date.now() + 1000; while (Date.now() < stop) { // wait } console.log("doThat - end"); } function doTheOther() { console.log("doThat - start & end"); }

.as-console.wrapper { max-height: 80% !important; }

<input type="button" id="btn" value="Click Me"> <p id="text"></p>

Ahora queremos hacer make doThat async ( nota : solo funcionará en un navegador reciente compatible con async / await , como Chrome; lamentablemente Stack Snippet''s Babel config no los incluye, por lo que no podemos usar esa opción):

var btn = document.getElementById("btn"); btn.addEventListener("click", handler, false); // handler can''t be async function handler(e) { console.log("handler triggered"); doSomething(); console.log("handler done"); } // doSomething can be async function doSomething() { doThis(); await doThat(); doTheOther(); } function doThis() { console.log("doThis - start & end"); } // make doThat async async function doThat() { console.log("doThat - start"); // simulate beginning async operation with setTimeout return new Promise(resolve => { setTimeout(() => { // do something that takes a while var stop = Date.now() + 1000; while (Date.now() < stop) { // wait } console.log("doThat - end (async)"); }, 0); }); } function doTheOther() { console.log("doThat - start & end"); }

.as-console.wrapper { max-height: 80% !important; }

<input type="button" id="btn" value="Click Me"> <p id="text"></p>

Lo más importante es que fuimos sincronizados tan pronto como pudimos, en doSomething (dado que el handler no puede ser asincrónico). Pero, por supuesto, eso cambia el momento del trabajo en relación con el controlador. (Por supuesto, probablemente deberíamos haber actualizado el handler para detectar errores de la promesa `devuelve doSomething ().)

1 Esa es la terminología de las especificaciones de JavaScript. La especificación HTML5 (que también toca esto) los llama "tareas" en lugar de "trabajos".


Hay un problema con su enfoque. En primer lugar, para que parte del código await a que termine la operación async , debe estar envuelto en una función async .

Por ejemplo:

async function asyncExample () { try { const response = await myPromise() // the code here will wait for the // promise to fullfil } catch (error) { // the code here will execute if the promise fails } } function nonAsyncExample () { asyncExample () console.log(''this will not wait for the async to finish'') // as it''s not wrapped in an async function itself }

Podría intentar declarar la función de autorun() como async , pero eso puede generar complicaciones adicionales.

Mi sugerencia, si su aplicación JS tiene un punto de entrada, se desencadena por un evento de onload , intente hacer su llamada ajax antes de este punto y luego almacenarla localmente en una variable y consultarla desde allí.

Por ejemplo, si su código se ve así:

function init () { // perform initialisations here } document.addEventListener("DOMContentLoaded", init)

cambiar eso para ser

document.addEventListener("DOMContentLoaded", function () { getAjaxConfig().then(function (response) { window.cookieStash = response init() } })

y obtenga sus datos del cookieStash en el resto de la aplicación. No necesitarás esperar por nada más.


Respuesta corta: no, no hay forma de hacer que el código asíncrono se ejecute sincrónicamente en JS como lo conoce desde C #. Hacer que todo sea asincrónico es una posible solución.

Sin embargo, como también controlas el lado del servidor, tengo otra sugerencia (bit de un truco): enviar la información requerida (contenido de la cookie) como metadatos de la solicitud, por ejemplo, como metaetiqueta HTML para solicitudes de página o encabezado de respuesta HTTP para XHR solicitudes, y almacenarlo en alguna parte.