jQuery的when.apply和包装异步函数

kbi*_*irk 0 javascript jquery asynchronous jquery-deferred

我有代码需要等待可变数量的服务器响应以及通过requireJS加载几个javascript模块.

我想利用jQuery的when.apply.不幸的是,我的代码的.then()部分总是在我的任何回调之前执行.

以下是我的代码摘要:

// load module func
function loadRequireJsModule(arg, callback) {
    require([arg], callback);
}

// get layer info from server func  
function getLayerInfoFromServer(arg, callback) {
    var layerInfoListener = new DataLayer([arg]);
    layerInfoListener.addRetrievedCallback(callback);
    layerInfoListener.retrieveLayerInfo();
}

tasks.push( $.Deferred( function() {
    loadRequireJsModule( "./" + layerJSON.type, function( module ) {
        layer.controller = module;
    });
}));

for (i=0; i<layerJSON.views.length; i++) {

    layer.views[i] = {};

    // get renderer class from require.js
    tasks.push( $.Deferred( function() {

        loadRequireJsModule( "./impl/" + layerJSON.views[i].renderer, function( index ){
            return function( module ) {
                layer.views[index].renderer = new module();
            }
        }(i));
    }));

    // POST request for layerInfo
    tasks.push( $.Deferred( function() {

        getLayerInfoFromServer( {
            request: "configure",
            layer:  layerJSON.layer,
            configuration: layerJSON.views[i]
        }, function( index ){
            return function( dataLayer, layerInfo ) {
                layer.views[index].dataService = new TileService( layerInfo );
            }
        }(i));
    }));
}

$.when.apply($, tasks).then( function() {
    ...
}
Run Code Online (Sandbox Code Playgroud)

我担心因为我的异步函数被包装,jQuery只是等待这些函数结束,这是立即的.我不能通过jquery传递裸异步函数,它们必须被包装.为了将我的功能视为"延期对象",我该怎么办?

jfr*_*d00 5

编辑 - 现在您已经披露了实际代码:

你没有$.Deferred()正确使用.

您的上下文中的典型用法如下所示:

var d;
var tasks = [];
for (var i = 0; i < len; i++) {
    // create new deferred object
    d = $.Deferred();
    // put deferred into tasks object
    tasks.push(d);
    loadRequireJsModule( "./impl/" + layerJSON.views[i].renderer, function( index, deferred ){
        return function( module ) {
            layer.views[index].renderer = new module();
            // now that this operation is done, resolve our deferred
            deferred.resolve();
        }
    }(i, d));
}
$.when.apply($, tasks).done(function() {
    // code executes here when all the deferreds 
    // have had `.resolve()` called on them
});
Run Code Online (Sandbox Code Playgroud)

然后,当放入任务数组的所有延迟对象得到解析(通过调用.resolve())时,$.when()将触发.

您通常不会将回调传递给$.Deferred()您正在执行的操作(用于其他内容,仅用于您希望以某种方式修改延迟对象的特殊情况).

您必须通过调用解析或拒绝它的延迟对象上的一个方法来解决或拒绝每个延迟的自己(有四种不同的方法可以使用).

另请注意,此结构并行运行所有异步任务(例如,它在开始时将它们全部触发,然后在所有这些任务完成时通知您).如果您希望它们在第一个完成之前没有启动第二个结构,那么您将需要一个不同的结构,依此类推.


如果你想要异步行为,那么$.when()期望它的参数是jQuery deferredpromise对象.因此,要使代码的结构起作用,返回myAsyncWrapper()必须是jQuery deferredpromise对象.这样,$.when()可以监视promise对象的完成情况,当它们全部完成时,调用你的.then()处理程序.

然后,您的异步操作必须解析或拒绝您传递给的每个延迟对象,$.when()因为只有.done()在您传递给它的所有延迟对象都已解析时,它才会调用它自己.或者,.fail()如果传递给它的任何延迟对象被拒绝,它将调用它自己.

如果您确定要传递给的所有参数$.when()都是延迟或承诺,并且您的.then()处理程序仍然被立即调用,那么您的延迟必须已经以某种方式解决,因为这可能是解释为什么.then()立即调用您的处理程序的原因.

如果没有传递给参数$.when()deferredpromise对象,那么它会自己解决问题立即从而打电话给你.then()立即处理这听起来像您所遇到的行为.


如果异步操作是ajax调用,则jQuery.ajax()已经返回一个promise对象,该对象将在ajax调用完成时解析.您可以直接将返回结果添加jQuery.ajax()到您的tasks数组并将其传递给$.when()它,它将支持您的异步行为.

使用jQuery.ajax(),这是使用$.when()多个并发ajax调用的概念:

var promises = [];
for (var i  = 0; i < myNum; i++) {
    promises.push($.ajax(...));
}
$.when.apply($, promises).done(function() {
    // put code here that runs when all the ajax calls have completed
    // the results of all the ajax calls are in
    // arguments[0], arguments[1], etc...
}).notify(function() {
    // .notify is purely optional and only necessary if you want to be notified as
    // each async operation completes
    // This will get called as each async operation completes with whatever arguments
    // were passed to the success handler
}).fail(function() {
    // this will get called immediately if any of the async operations failed
    // the arguments are the same as the .done() function except that some may be empty
    // if they didn't complete before one of them failed
});
Run Code Online (Sandbox Code Playgroud)

很显然,你不必使用所有这三个操作.done(),.notify()并且.fail()也可以指定它们都在.then()-我只是包含所有三个与教育目的的意见.


如果您需要有关其他类型的异步操作的帮助,但尚未创建自己的promise对象,那么如果您显示并描述实际操作,我将很乐意为您提供帮助.延迟很容易在最初得到处理,但是很多东西实际上非常简单,并且一旦最终沉入其中就非常有用.