11

我正在使用一个名为的 jQuery 库bootbox

bootbox.dialog({
    title: "Group",
    buttons: {
        success: {
            label: "OK",
            className: "btn-success",
            callback: function () {
                postForm();
            }
        }
    }
});

function postForm() {        
    $.ajax({
        type: "POST",
        url: $("#add-group").val(),
        data: $("#form").serialize(),
        success: function (data) {
            return true;
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            return false;
        }
    });
}

当我单击“确定”按钮时,除非我返回如下错误值:

callback: function () {
    return false;
}

对话框将关闭。

在回调函数中,我调用postForm的是一个对我的服务器端进行 AJAX 调用以执行某些操作的函数。根据该操作的结果,我想保持对话框仍然打开。

但是因为它是一个 AJAX 调用,并且结果需要一段时间才能回来,所以postForm()无论我的结果如何,对话框都会立即关闭。

如何告诉 javascript 等待 ajax 调用的结果?

如果我能做这样的事情会很棒:

callback: function () {
    var result = await postForm();
    return result;
}
4

7 回答 7

8

JavaScript(当前)没有与async/等效的语言await。有多种适用于 JavaScript 的 Promise 库Task,它们为您提供了该类型的大致等价物。这是在原始回调之上的一个很好的步骤,但是除了最简单的场景之外,您仍然会遇到尴尬的嵌套或回调意大利面。

JavaScript ECMAScript 6(“Harmony”)预计将包含生成器。ES6 有望在今年晚些时候正式发布,但在此之后可能需要一段时间才能安全地假设用户的浏览器支持生成器。

通过将生成器与 promise 结合起来,您可以实现真正的async/await等价的。

于 2014-04-24T12:57:31.170 回答
8

I think Promises is exactly what you are asking.

.promise()

Return a Promise object to observe when all actions of a certain type bound to the collection, queued or not, have finished. e.g.

var div = $( "<div>" );

div.promise().done(function( arg1 ) {
  // Will fire right away and alert "true"
  alert( this === div && arg1 === div );
});

For more info refer : https://api.jquery.com/promise/

Deferred Promise is closer to async behaviour:

deferred.promise()

The deferred.promise() method allows an asynchronous function to prevent other code from interfering with the progress or status of its internal request. The Promise exposes only the Deferred methods needed to attach additional handlers or determine the state (then, done, fail, always, pipe, progress, and state), but not ones that change the state (resolve, reject, notify, resolveWith, rejectWith, and notifyWith).

If target is provided, deferred.promise() will attach the methods onto it and then return this object rather than create a new one. This can be useful to attach the Promise behavior to an object that already exists.

If you are creating a Deferred, keep a reference to the Deferred so that it can be resolved or rejected at some point. Return only the Promise object via deferred.promise() so other code can register callbacks or inspect the current state.

Example:

function asyncEvent() {
  var dfd = new jQuery.Deferred();

  // Resolve after a random interval
  setTimeout(function() {
    dfd.resolve( "hurray" );
  }, Math.floor( 400 + Math.random() * 2000 ) );

  // Reject after a random interval
  setTimeout(function() {
    dfd.reject( "sorry" );
  }, Math.floor( 400 + Math.random() * 2000 ) );

  // Show a "working..." message every half-second
  setTimeout(function working() {
    if ( dfd.state() === "pending" ) {
      dfd.notify( "working... " );
      setTimeout( working, 500 );
    }
  }, 1 );

  // Return the Promise so caller can't change the Deferred
  return dfd.promise();
}

// Attach a done, fail, and progress handler for the asyncEvent
$.when( asyncEvent() ).then(
  function( status ) {
    alert( status + ", things are going well" );
  },
  function( status ) {
    alert( status + ", you fail this time" );
  },
  function( status ) {
    $( "body" ).append( status );
  }
);

For more information, see the documentation for Deferred object: http://api.jquery.com/category/deferred-object/

jQuery.when()

Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events.Example:

$.when( $.ajax( "test.aspx" ) ).then(function( data, textStatus, jqXHR ) {
  alert( jqXHR.status ); // Alerts 200
});
于 2014-04-24T08:29:37.640 回答
5

你不能。await在 JS中没有等价物。

您必须通过在调用时返回 false 来模拟它,postForm然后在从 AJAX 调用执行回调函数时关闭对话框。

编辑/更新:从 ES2017 开始,有 async/await 支持 - 虽然我不知道它是否与 jQuery 一起工作。

于 2014-04-24T08:19:33.307 回答
5

随着 ES2017 的出现,这个问题的答案是 async/await。
这是“回调地狱”的 JS 解决方案。Promise 等价于 System.Threading.Tasks.Task。您可以在异步函数中等待 Promise。
与 C# 不同,无法在同步函数中调用异步函数。
因此,您可以在async函数中等待Promise ,并且只能在async函数中。

async function foo()
{
     return 123;
}

let result = await foo();
console.log(result)

您可以使用 TypeScript 或 babel 将 async/await 转译回 ES5(当 IE11 存在时)。
IE11 有一个 promise polyfill。
有关详细信息,请参阅ECMA 草案 262MDN

promise 的一个很好的例子是 FETCH api。
fetch-API 适用于 async/await ajax 请求。
为什么 ?因为如果你必须承诺 XmlHttpRequest,它看起来像这样:

<!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 ajaxGet(url, data)
        {
            var result;

            return new Promise(function (resolve, reject)
                {

                    ajax.get(url, data, function (bError, strMessage, iStatus)
                        {

                            // console.log("args:", arguments);

                            // console.log("Error:", bError);
                            // console.log("Message:", strMessage);
                            // console.log("Status:", iStatus);

                            result = bError;
                            resolve(result);
                        }
                        ,true
                    );

                }
            );

        }


        async function main()
        {
            var ajaxResult = await ajaxGet("./ajax/json.ashx");
            console.log("ajaxResult: ", ajaxResult);
        }

        async function lol() 
        { 
            var res = null;

            var myPromise = new Promise(function (resolve, reject)
            {
                // Standard AJAX request setup and load.
                var request = new XMLHttpRequest();

                // Request a user's comment from our fake blog.
                request.open('GET', 'https://localhost:57566/ajax/json.ashx');

                /*
                // Set function to call when resource is loaded.
                // Onload same as onreadystatechange - onload added with XHR2
                request.onload = function ()
                {
                    // internal server error/404
                    if (request.status === 200)
                    {
                        res = request.response;
                        // console.log(request.response);
                        console.log("onload- resolving promise");
                        resolve(request.response);
                    } else
                    {
                        console.log("onload- rejectinv promise");
                        reject('Page loaded, but status not OK.');
                    }
                };
                */


                request.onreadystatechange = function ()
                {
                    console.log("readystate:", request.readyState);
                    console.log("status:", request.status)

                    if (request.readyState != 4) return;

                    // XMLHttpRequest.DONE = 200, 0=cancelled 304 = redirect
                    //if (!(request.status != 200 && request.status != 304 && request.status != 0))
                    if (request.status === 200)
                    {
                        console.log("successy")
                        resolve(request.responseText); // Success 
                        return;
                    }

                    if (request.status != 200 && request.status != 0 && request.status != 304)
                    {
                        console.log('HTTP error ' + request.status);
                        // reject('Page loaded, but status not OK.');
                        reject(new Error("Server error - Status NOK", "filename", "linenum666")); // Error 
                        return;
                    }

                    if (request.status === 0)
                    {
                        console.log("cancelled:", request)
                        //resolve(null); // Cancelled, HTTPS protocol error
                        return;
                    }

                    reject(new Error("Strange error", "filename", "linenum666")); // Some Error 
                };

                // Set function to call when loading fails.
                request.onerror = function ()
                {
                    // Cannot connect 
                    console.log("OMG OnError");
                    // reject('Aww, didn\'t work at all. Network connectivity issue.');
                    reject(new Error("Aww, didn\'t work at all. Network connectivity issue.", "filename", "linenum666")); // Some Error 

                };


                if (!navigator.onLine)
                {
                    console.log("No internet connection");
                    reject("No internet connection");
                }
                else
                {
                    try
                    {
                        request.send();
                    }
                    catch (ex)
                    {
                        console.log("send", ex.message, ex);
                    }
                }

            });

            return myPromise;
        }



        async function autorun()
        {
            console.clear();
            // await main();

            try
            {
                var resp = await lol();
                console.log("resp:", resp);
            }
            catch (ex)
            {
                console.log("foo", ex.message, ex);
            }



            console.log("I am here !");
        }

        if (document.addEventListener) document.addEventListener("DOMContentLoaded", autorun, false);
        else if (document.attachEvent) document.attachEvent("onreadystatechange", autorun);
        else window.onload = autorun;
    </script>

</body>
</html>
于 2017-07-05T12:09:22.450 回答
3

请参阅Stefan关于现在使用 ES2015 async/await 的回答。


原始答案

您可以考虑使用 asyncawait,它可以有效地让您编写如下代码

var foo = async (function() {
    var resultA = await (firstAsyncCall());
    var resultB = await (secondAsyncCallUsing(resultA));
    var resultC = await (thirdAsyncCallUsing(resultB));
    return doSomethingWith(resultC);
});

而不是以下

function foo2(callback) {
    firstAsyncCall(function (err, resultA) {
        if (err) { callback(err); return; }
        secondAsyncCallUsing(resultA, function (err, resultB) {
            if (err) { callback(err); return; }
            thirdAsyncCallUsing(resultB, function (err, resultC) {
                if (err) {
                    callback(err);
                } else {
                    callback(null, doSomethingWith(resultC));
                }
            });

        });
    });
}
于 2015-03-22T03:04:02.680 回答
2

您可以使用 ES6 生成器和Google Traceur Compileryield的功能,正如我在此处描述的那样。

Traceur 团队有一些反馈表明它是生产质量的工具。它还async/await. _

于 2014-04-24T23:37:41.997 回答
1

虽然这并没有回答“C# 在 JavaScript 中的等待是什么?” 问题,问题中的代码可以很容易地工作。该bootbox.dialog函数返回一个对象,因此您可以调整显示的代码,如下所示:

var dialog = bootbox.dialog({
    title: "Group",
    buttons: {
        success: {
            label: "OK",
            className: "btn-success",
            callback: function () {
                postForm();
                return false; // add this return here
            }
        }
    }
});

然后将ajax调用调整为:

function postForm() {        
    $.ajax({
        type: "POST",
        url: $("#add-group").val(),
        data: $("#form").serialize(),
        success: function (data) {
            // add this call to the underlying Bootstrap modal object
            dialog.modal('hide'); 
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            // Maybe inject an error message into the dialog?
        }
    });
}
于 2015-07-11T02:47:06.320 回答