当前位置: 首页 > 面试题库 >

如何在同步功能中等待JavaScript中的异步调用?

万俟鸿波
2023-03-14
问题内容

最近,我不得不更正Web应用程序(我没有创建)中的安全性问题。安全问题是,它正在使用非仅限HTTP的cookie。因此,我不得不将session-
cookie设置为仅http-,这意味着您无法再从javascript中读取(设置)cookie的值。到目前为止,接缝都很容易。

更深层的问题是,使用的Web应用程序

JSON.parse(readCookie(cookieName)).some_value

在一百万个地方

因此,为了不必重写“一百万行代码”,我不得不创建一个ajax端点,该端点使我将http-cookie的内容作为JSON并重写readCookie以使用
SYNCHRONOUS ajax请求(而不是读取cookie)
),因为其余可怕的代码希望readCookie在这百万个地方是同步的,因为读取cookie是同步的。

现在的问题是,我得到了很多

不赞成在主线程上使用同步XMLHttpRequest,因为它会对最终用户的体验产生不利影响。如需更多帮助,请访问https://xhr.spec.whatwg.org/。

它会向调试控制台发送垃圾邮件,更不用说有人决定删除此功能了。

因此,我正在研究新的ES async / await关键字,以了解这是否可以以某种方式帮助同步进行异步ajax请求(我知道我必须为IE 11使用包装器)。

到目前为止,我已阅读以下页面
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
/功能 *

但看起来所有新的异步内容似乎仅能解决编写异步代码的问题,而不能在异步代码与现有同步代码之间实现互操作。使用我读取的信息,我现在可以像异步同步一样等待异步ajax调用的结果,但是问题是-
等待仅在异步方法中允许…这意味着即使我可以像这样等待结果如果是同步的,则getCookie方法仍然必须是异步的,这使所有内容看起来都是毫无意义的(除非整个代码都是异步的,当您不是从头开始时肯定不是这样)。

我似乎找不到任何有关如何在同步和异步代码之间进行互操作的信息。

例如,在C#中,我可以使用.Result从同步上下文中调用异步方法,例如

 AsyncContext.RunTask(MyAsyncMethod).Result;

或更容易但死锁安全性较低,例如

MyAsyncMethod(args).Result;

有什么方法可以在JavaScript中实现相同的目标吗?

当其余的代码库是同步的,没有互操作的可能性时,异步传播似乎没有任何意义。难道真的没有办法在2017 AD的JavaScript中实现这一目标吗?

我再次强调
我知道 如何进行同步ajax调用,并且 知道 如何将异步ajax调用与回调和/或promise一起使用。
但是我无法弄清楚如何同步async-ajax-call (无回调), 以便可以从期望 同步 运行的代码(“百万个位置”)中使用它!

到目前为止,这是我尝试过的操作:(
请注意,无论我使用 loadQuote 还是 main 在调试控制台中仍会首先显示 文本 “ Ron曾经说过” ,如果
异步 ajax调用已得到 同步 解决,则不会出现这种情况)

<!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的运行到完成语义 要求
在任何未决的异步操作(例如,对XHR异步调用的XHR处理程序的回调)能够运行之前,同步功能必须完成。

JavaScript在给定线程上运行的方式是它处理作业队列1:

  1. 接下一个待处理的工作
  2. 同步执行该作业的代码
  3. 只有当该工作完成后,才返回到步骤1接下一个工作

(这要复杂一点,它有两个层次,但这与这个特定问题无关。)

XHR补全等都是在队列中安排的作业。无法暂停作业,从队列中运行另一个作业以及拾取已暂停的作业。async/ 为处理异步操作await提供了非常简单的
语法 ,但它们不会改变作业队列的性质。

对于您的情况,我看到的唯一解决方案是一直一直到顶层。这可能没有您想像的那么复杂(也许会如此)。在许多情况下,它是asyncfunction许多功能之前添加的。但是,使这些功能异步可能会产生重大的连锁反应(例如,在事件处理程序中同步的事物变为异步的事物会更改与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/ 的最新浏览器上才能使用await,例如Chrome;遗憾的是Stack
Snippet的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以捕获来自promise`doSomething()返回的错误。)

1这是JavaScript规范术语。HTML5规范(也涉及到此规范)称它们为“任务”,而不是“工作”。



 类似资料:
  • 问题内容: 据我了解,在ES7 /ES2016中,将多个in放在代码中的工作方式类似于带有promise的链接,这意味着它们将一个接一个地执行而不是并行执行。因此,例如,我们有以下代码: 我是否正确理解仅在完成时才会调用?并行调用它们的最优雅方式是什么? 我想在Node中使用它,所以也许有一个异步库解决方案? 编辑:我对这个问题提供的解决方案不满意:减速是由于异步生成器中非并行等待Promise的

  • 我正在尝试做一个简单的应用程序,加载数据并对其执行一个操作.所以我的想法是做这个异步。 我有3个数据源,我想异步加载它们。例如data1.xml、data2.xml和data3.xml所有文件加载起来都相当大,所以需要一些时间(这就是为什么我想要异步的原因)。 例如,我创建了一个包含3个文本框的窗口,这些文本框都绑定到一个特定的属性(Text1、Text2、Text3)和一个按钮。当我点击按钮时,

  • 问题内容: 我写了这段代码 然后我尝试在另一个文件中使用它 我有一个错误 “等待仅在异步功能中有效” 有什么问题 问题答案: 错误不是指而是。 我利用这个问题的机会来告诉你一个已知的反模式的使用方法:。 错误 正确 另外,要知道有一个正确且重要的特殊情况:(使用try / catch)

  • 问题内容: 我正在尝试使用新的异步功能,希望解决我的问题以后能对其他人有所帮助。这是我的代码正在工作: 问题是,我的while循环运行得太快,脚本每秒向Google API发送太多请求。因此,我想构建一个睡眠函数以延迟请求。因此,我也可以使用此功能来延迟其他请求。如果还有其他方法可以延迟请求,请告诉我。 无论如何,这是我的新代码不起作用。请求的响应在setTimeout中返回给匿名异步函数,但是我

  • 我试图理解为什么这段代码返回“Promise{pending}”。 当我通过reduceDirections()函数时,我可以看到我得到了想要的结果。但是当我(一行之后)时,我会改为“Promise pending” 很抱歉,我不理解promise和异步等待。我曾尝试在mdn上阅读和观看视频,但我不知道如何将它们显示的内容转移到这个问题上。提前感谢您的帮助!

  • 问题内容: 我有一个在另一个类中启动异步任务,然后应该等待结果。 问题是方法将在方法运行完成后立即完成,对吗? 这意味着,通常,在启动异步任务后,它将立即关闭,并且不再在那里接收结果。 如何使以上代码段正常工作?开始任务后,我已经尝试放入(任意持续时间)。似乎可以正常工作。 但这绝对不是一个干净的解决方案。也许甚至有一些严重的问题。 有更好的解决方案吗? 问题答案: 使用标准类代替,从回调启动异步