jQuery deferred如何使用?

284

jQuery 1.5引入了新的Deferred对象以及附带的方法 .when, .Deferred._Deferred

如果你之前没有使用过 .Deferred,我已经为它的源码添加了注释

这些新方法的可能用法是什么?我们该如何将它们应用到模式中呢?

我已经阅读了API源代码,所以我知道它们是干什么的。我的问题是:我们如何在日常代码中使用这些新特性呢?

我有一个简单的示例,演示了按顺序调用 AJAX 请求的缓冲类。(下一个请求在上一个请求完成后开始执行。)

/* Class: Buffer
 *  methods: append
 *
 *  Constructor: takes a function which will be the task handler to be called
 *
 *  .append appends a task to the buffer. Buffer will only call a task when the 
 *  previous task has finished
 */
var Buffer = function(handler) {
    var tasks = [];
    // empty resolved deferred object
    var deferred = $.when();

    // handle the next object
    function handleNextTask() {
        // if the current deferred task has resolved and there are more tasks
        if (deferred.isResolved() && tasks.length > 0) {
            // grab a task
            var task = tasks.shift();
            // set the deferred to be deferred returned from the handler
            deferred = handler(task);
            // if its not a deferred object then set it to be an empty deferred object
            if (!(deferred && deferred.promise)) {
                deferred = $.when();
            }
            // if we have tasks left then handle the next one when the current one 
            // is done.
            if (tasks.length > 0) {
                deferred.done(handleNextTask);
            }
        }
    }

    // appends a task.
    this.append = function(task) {
        // add to the array
        tasks.push(task);
        // handle the next task
        handleNextTask();
    };
};

我正在寻找 .Deferred.when 的示例和可能的用途。

如果能提供 ._Deferred 的示例就更好了。

链接到新的jQuery.ajax源文件以获取示例是不允许的。

我特别感兴趣的是,在抽象化操作是否同步或异步完成时可用的技术。


19
常见问题解答中提到:避免询问主观性强、每个回答都同样合理的问题,例如“你最喜欢的 ______ 是什么?”(原文有强调) - T.J. Crowder
2
@T.J.Crowser 我会考虑重新措辞。 - Raynos
5
这是一个很好的问题,但可能没有那么多人能够回答 :-) - Pointy
2
@Pointy 我主要关注那些在它还是第三方插件时使用过它的人。并鼓励大家坐下来使用它! - Raynos
1
._Deferred只是.Deferred使用的真正的“延迟对象”。它是一个内部对象,你很可能永远不会需要它。 - David Tang
显示剩余4条评论
11个回答

1

我刚刚在实际代码中使用了Deferred。在项目jQuery Terminal中,我有一个函数exec,它调用用户定义的命令(就像用户输入并按下回车键一样),我已经将Deferreds添加到API中,并使用数组调用exec。像这样:

terminal.exec('command').then(function() {
   terminal.echo('command finished');
});

terminal.exec(['command 1', 'command 2', 'command 3']).then(function() {
   terminal.echo('all commands finished');
});

这些命令可以运行异步代码,并且exec需要按顺序调用用户代码。我的第一个API使用了暂停/恢复调用对,而在新的API中,当用户返回promise时会自动调用它们。因此,用户代码可以直接使用。

return $.get('/some/url');

或者

var d = new $.Deferred();
setTimeout(function() {
    d.resolve("Hello Deferred"); // resolve value will be echoed
}, 500);
return d.promise();

我使用类似这样的代码:

exec: function(command, silent, deferred) {
    var d;
    if ($.isArray(command)) {
        return $.when.apply($, $.map(command, function(command) {
            return self.exec(command, silent);
        }));
    }
    // both commands executed here (resume will call Term::exec)
    if (paused) {
        // delay command multiple time
        d = deferred || new $.Deferred();
        dalyed_commands.push([command, silent, d]);
        return d.promise();
    } else {
        // commands may return promise from user code
        // it will resolve exec promise when user promise
        // is resolved
        var ret = commands(command, silent, true, deferred);
        if (!ret) {
            if (deferred) {
                deferred.resolve(self);
                return deferred.promise();
            } else {
                d = new $.Deferred();
                ret = d.promise();
                ret.resolve();
            }
        }
        return ret;
    }
},

dalyed_commands被用于恢复函数中,该函数再次调用exec并使用所有dalyed_commands。

同时也是commands函数的一部分(我去掉了不相关的部分)。

function commands(command, silent, exec, deferred) {

    var position = lines.length-1;
    // Call user interpreter function
    var result = interpreter.interpreter(command, self);
    // user code can return a promise
    if (result != undefined) {
        // new API - auto pause/resume when using promises
        self.pause();
        return $.when(result).then(function(result) {
            // don't echo result if user echo something
            if (result && position === lines.length-1) {
                display_object(result);
            }
            // resolve promise from exec. This will fire
            // code if used terminal::exec('command').then
            if (deferred) {
                deferred.resolve();
            }
            self.resume();
        });
    }
    // this is old API
    // if command call pause - wait until resume
    if (paused) {
        self.bind('resume.command', function() {
            // exec with resume/pause in user code
            if (deferred) {
                deferred.resolve();
            }
            self.unbind('resume.command');
        });
    } else {
        // this should not happen
        if (deferred) {
            deferred.resolve();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接