jQuery 异步编程模式?

jQuery Asynchronous programming pattern?

我继承了一个扫描页面以查找数据属性并相应地修改 dom、添加处理程序等的应用程序。

我被要求实现动态标记,由此我发出一个 ajax 调用以获取 json 数据,然后使用 Handlebars 根据返回的数据呈现新的 html。

我需要告诉主要的外部应用程序等到所有 json 调用都已解决,然后再去寻找数据属性。

我应该采用哪种模式来执行此操作?

非常感谢任何帮助。

谢谢, 斯科特

更新

outer.js - 扫描 DOM 寻找要做的事情。

inner.js - 发出 0-9 ajax 调用(取决于我所在的页面)。每个 ajax 调用都有一个 .then() 函数调用 Handlebars 函数来编写标记。

如果我只有一个 ajax 调用,我可以将 inner.js 作为延迟传回给 outer.js。然后,在 ajax 的 .then() 函数中,我将调用 inner.js.resolve().

但是如果我对 9 个可能的每一个都这样做,inner.js 将在 #1 运行之后但在#2 完成之前解决。

如何动态设置 outer.js 用于等待它们全部解决的延迟数?

像这样延迟绑定数组似乎是不可能的:

在 inner.js 的顶部:

var application = jQuery.Deferred();
var arrayOfAsync = [];
application.done(arrayOfAsync);

然后,当在页面上找到 jQuery 个选择器时,我可以执行

arrayOfAsync.push(localPromise);

但这似乎不起作用……应该吗?这将假设 Deferred() 实际上只是保留一个普通数组,它可能不是...

在这种情况下你应该使用 promises。

假设您有 车把 模板:

<script id="PostsTempl" type="text/x-handlebars-template">
    {{#each posts}}
        <li class="item-handlebars">{{body}}</li>
    {{/each}}
</script>

其中应填充从网络 api/web 服务获取的一系列帖子。

您将在使用前编译模板:

var myTemplate = Handlebars.compile($("#PostsTempl").html());

现在我们需要一个函数来调用 Web api/web 服务并获取一些数据:

function fetchData()
{
    var deferred = $.Deferred();

    $.ajax({
        type: 'GET',
        dataType: 'json',
        url: 'my/api/posts/1/20',
        data: {},
        success: function (jsonData) {
           if (jsonData) {
              deferred.resolve(jsonData);
           } else {
              deferred.reject('');
           }
        },
        error: function (req, status, error) {
            var errorMessage = (error.message) ? error.message : error;
            deferred.reject(errorMessage);
        }
    });

    return deferred.promise();

}

我们将使用 $.ajaxGET 数据。我们在这里定义了一个promise

var deferred = $.Deferred();

这将在我们取回数据时解决:

success: function (jsonData) {
    if (jsonData) {
       deferred.resolve(jsonData);
    } else {
       deferred.reject('');
    }
},

或者如果没有数据,最终会被拒绝。

return deferred.promise(); 会 return 我们的承诺。

现在我们可以调用解析 promise 并反馈一些数据的函数:

  fetchData()
    .then(function(data){
        // console.log(data);
        var posts = {posts: data};
        $("#posts").append(myTemplate(posts));
        return true;
    })
    .then(function(result){
        goLookForDataAttributes();
    })
    .fail(function (reason) {
        if (reason !== '') {
            alert('Something went wrong:' + reason);
        }
    }); 

当我们取回数据时,我们将这些项目附加到我们的模板中:

.then(function(data){
    // console.log(data);
    var posts = {posts: data};
    $("#posts").append(myTemplate(posts));
    return true;
})

当一切都完成后,我们在另一个 .then() 分支中调用另一个函数:

   .then(function(result){
        goLookForDataAttributes();
    })

Promise 可以链接。 第二个 .then() 在第一个执行后被调用。

这是您需要的最后一点:

function goLookForDataAttributes()
{
    $('#posts li.item-handlebars').each(function (index, item) {
        $(item).addClass('big-font');
    });
}

这篇 fiddle 可能会对您有所帮助。

在这个例子中,我解析帖子并在 handlebards 呈现元素时添加 class。

更新:

由于您正在调用 Web api/web 服务,因此您可以并行执行承诺,等待所有 ajax 请求完成并执行您的最后一个方法。

为了简单起见,我将创建一个虚假的承诺(应该是您的 ajax 请求):

function buildPromise(id)
{
    var deferred = $.Deferred();

    setTimeout(function(){
        var data = {id: id, name: 'name: ' + id};
        deferred.resolve(data);
    }, 1000);

    return deferred.promise();
}

然后我将创建一个包含 10 个承诺的数组:

var promises = [];

for (var p = 0; p < 10; p++)
{
    promises.push(buildPromise(p));
}

现在我将能够运行并行实现所有这些承诺:

$.when.apply($, promises)
    .then(function () {
        for(var i = 0; i < arguments.length; i++) {
            $('#content').append('<p>' + arguments[i].name + '</p>');
        }
    }).then(function(results) {
    return finalPromise();
})
.then(function(result){
    alert('success: ' + result.success);
    alert('Finished');
});

$.when.apply($, promises) 并行解决所有承诺,并在我们取回所有结果时 returns。
结果可以在 arguments 中找到,可以使用数组的索引 arguments[x].

读取

当所有 ajax 请求都已执行后,我们将调用 finalPromise:

function finalPromise()
{
    var deferred = $.Deferred();

    setTimeout(function(){
        var data = { success: true };
        deferred.resolve(data);
    }, 1000);

    return deferred.promise();

}

finalPromise 也可以是一个没有承诺的常规函数​​。

This就是这个样子

当然现在你得根据自己的情况进行调整了。