programing

자바스크립트에서 비동기 호출을 동기 함수에서 기다리는 방법은 무엇입니까?

mailnote 2023. 7. 25. 21:12
반응형

자바스크립트에서 비동기 호출을 동기 함수에서 기다리는 방법은 무엇입니까?

최근에 만들지 않은 웹 응용프로그램에서 보안 문제를 해결해야 했습니다.보안 문제는 http 전용이 아닌 쿠키를 사용한다는 것이었습니다.그래서 저는 세션 쿠키 http-only를 설정해야 했습니다. 즉, 자바스크립트에서 쿠키의 값을 더 이상 읽을 수 없고 설정할 수 없습니다.지금까지는 아주 쉽습니다.

더 깊은 문제는 사용된 웹 애플리케이션입니다.

JSON.parse(readCookie(cookieName)).some_value

백만 군데에

그래서 "100만 줄의 코드"를 다시 쓸 필요가 없기 위해 저는 http-cookie의 콘텐츠를 JSON으로 제공하는 아약스 엔드포인트를 만들고 (쿠키를 읽는 대신) synchronous ajax 요청을 사용하도록 readCookie를 다시 써야 했습니다. 나머지 끔찍한 코드는 읽기 쿠키가 이 백만 개의 장소에서 동기화될 것으로 예상하기 때문입니다.쿠키를 읽는 것은 동기적이기 때문입니다.

지금 문제는, 저는 많은 것을 얻는다는 것입니다.

메인 스레드에서 동기식 XMLHttpRequest는 최종 사용자의 환경에 해로운 영향을 미치기 때문에 더 이상 사용되지 않습니다.자세한 도움말은 https://xhr.spec.whatwg.org/ 을 참조하십시오.

누군가 이 기능을 제거하기로 결정했을 가능성은 말할 것도 없고 디버그 콘솔을 스팸 처리합니다.

따라서 저는 새로운 ES async/wait 키워드를 알아보고 비동기식 Ajax-request를 동기화하는 데 도움이 될 수 있는지 알아보고 있습니다(IE 11에 래퍼를 사용해야 한다는 것을 알고 있습니다).

지금까지, 나는 이 페이지들을 읽었습니다.
https://www.twilio.com/blog/2015/10/.htmlhttps ://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html
https://pouchdb.com/2015/03/05/.htmlhttps ://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html
https://jakearchibald.com/2014//https://jakearchibald.com/2014/es7-async-functions/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

그러나 모든 새로운 비동기식은 비동기식 코드와 기존 동기식 코드 간의 상호 운용을 가능하게 하지 않고 비동기식 코드를 더 쉽게 작성하는 문제를 충족시키는 것처럼 보입니다.제가 읽은 정보를 사용하여, 저는 이제 동기식이었던 것처럼 비동기식 아약스 호출의 결과를 기다릴 수 있지만, 문제는 - wait는 비동기식 방법에서만 허용된다는 것입니다.즉, 동기식인 것처럼 결과를 기다릴 수 있다고 해도 getCookie 메서드는 여전히 비동기식이어야 하므로 모든 것이 완전히 무의미한 것처럼 보입니다(처음부터 시작하지 않을 때는 확실히 비동기식이 아닙니다).

동기식 코드와 비동기식 코드 사이에서 상호 작용하는 방법에 대한 정보를 찾을 수 없습니다.

예를 들어, C#의 경우 동기화 컨텍스트에서 비동기 메서드를 호출할 수 있습니다.결과, 예:

 AsyncContext.RunTask(MyAsyncMethod).Result;

더 쉽지만 덜 교착 상태 - 안전한 것처럼.

MyAsyncMethod(args).Result;

자바스크립트에서 동일한 것을 달성할 수 있는 방법이 있습니까?

비동기식을 퍼뜨리는 건 말이 안 되는 것 같은데, 코드베이스의 나머지 부분이 동기식이고 상호작용의 가능성이 없다면...AD 2017년에 자바스크립트에서 이것을 달성할 방법이 정말로 없습니까?

다시 한 번 강조합니다.
동기식 아약스 호출을 하는 방법을 알고 있으며 콜백 및/또는 약속과 함께 비동기식 아약스 호출을 사용하는 방법을 알고 있습니다.
하지만 제가 이해할 수 없는 것은 비동기-아약스-콜(콜백 없음)을 동기화하는 방법입니다. 그래서 그것은 동기적으로 실행될 것으로 예상되는 코드에서 사용될 수 있습니다. ("백만 개의 장소"에서)!

입니다: 이이제지가시지것도입다니한금까것▁this것다.
하십시오.) loadQuote 또는 main"Ron onece said"라는 텍스트는 여전히 디버그 콘솔에 처음 나타납니다. 비동기 Ajax-call이 동기적으로 해결된 경우에는 해당되지 않습니다.)

<!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>

하지만 문제는 - 대기가 비동기식에서만 허용된다는 것입니다.

맞아요, 그리고 아니요, 그것에 대한 해결책은 없습니다.JavaScript의 Run-to-Complete 의미론은 보류 중인 비동기 작업(예: 비동기 XHR 호출에 대한 XHR 핸들러 콜백)이 실행되기 전에 동기화 기능을 완료해야 합니다.

지정된 스레드에서 JavaScript를 실행하는 방법은 작업1 대기열을 처리하는 것입니다.

  1. 보류 중인 다음 작업 선택
  2. 해당 작업에 대한 코드를 동기화하여 실행
  3. 해당 작업이 완료된 경우에만 1단계로 돌아가 다음 작업을 선택합니다.

(그것보다 조금 더 복잡합니다. 두 가지 수준이 있지만, 그것은 이 특정 질문과 관련이 없습니다.)

XHR 완료 등은 대기열에서 예약되는 작업입니다.작업을 일시 중지하고 대기열에서 다른 작업을 실행한 다음 일시 중지된 작업을 선택할 수 없습니다.async/await에서는 비동기 작업을 처리하기 위한 매우 간단한 구문을 제공하지만 작업 대기열의 특성은 변경되지 않습니다.

당신의 상황에 대한 유일한 해결책은 비동기식으로 최고 수준까지 가는 것입니다.이것은 당신이 생각하는 것만큼 복잡하지 않을 수도 있습니다(또는 그렇게 될 수도 있습니다)많은 경우에 그것은 추가하고 있습니다.async앞에function다방면으로그러나 이러한 기능을 비동기식으로 만들면 상당한 노크 효과가 발생할 수 있습니다(예를 들어, 이벤트 핸들러에서 동기화된 것이 비동기식이 되는 것은 UI와 관련하여 발생하는 작업의 타이밍을 변경합니다.

예를 들어, 이 동기 코드를 생각해 보십시오.

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>

이제 우리는 만들기를 원합니다.doThat비동기(참고: 지원하는 최신 브라우저에서만 작동합니다.async/awaitChrome과 같이; 슬프게도 Stack Snipet의 Babel 구성은 그것들을 포함하지 않기 때문에, 우리는 그 옵션을 사용할 수 없습니다.):

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>

거기서 중요한 것은 우리가 할 수 있는 한 빨리 비동기화되었다는 것입니다.doSomething(이후handler비동기일 수 없음).하지만 물론, 그것은 핸들러와 관련하여 작업의 타이밍을 변경합니다. (물론, 우리는 아마도 업데이트를 했어야 했습니다.)handler'doSomething() returns'라는 약속에서 오류를 감지합니다.


1 이것이 자바스크립트 사양 용어입니다.HTML5 사양(이것에 대해서도 언급함)은 이를 "작업" 대신 "작업"이라고 부릅니다.

당신의 접근 방식에 문제가 있습니다.에서 째첫까지, 코의일부대해는서에드▁first는대서해▁to.await위해서async완료하려면 작업 자체를 a로 포장해야 합니다.async기능.

예:

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
}

다음과 같이 선언할 수 있습니다.autorun()을 합니다.async하지만 그것은 추가적인 합병증으로 이어질 수 있습니다.

은 제제은JS있다면이그것은, 안점앱에 의해 됩니다.onload이벤트, 이 지점 이전에 Ajax 호출을 수행한 다음 로컬 변수에 저장하고 거기서 쿼리하십시오.

예를 들어 코드가 다음과 같은 경우:

function init () {
    // perform initialisations here
}

document.addEventListener("DOMContentLoaded", init)

그것을 …로

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

그리고 당신의 데이터를 에서 얻습니다.cookieStash나머지 부분에 적용됩니다.다른 것을 기다릴 필요가 없습니다.

간단한 답변: C#에서 알고 있는 것처럼 JS에서 비동기 코드를 동기식으로 실행할 수 있는 방법은 없습니다.모든 것을 비동기식으로 만드는 것이 가능한 해결책입니다.

그러나, 당신이 서버 측면도 통제하고 있기 때문에, 나는 또 다른 제안을 합니다: 페이지 요청을 위한 HTML 메타 태그 또는 XHR 요청을 위한 HTTP 응답 헤더와 같은 요청의 메타데이터로서 필요한 정보(쿠키 콘텐츠)를 함께 보내고, 그것을 어딘가에 저장하는 것입니다.

언급URL : https://stackoverflow.com/questions/41976606/how-to-await-an-async-call-in-javascript-in-a-synchronous-function

반응형