包装jQuery.ajax:级联内部jqXHR作为一个新的延迟对象返回
Wrapping jQuery.ajax: Cascading inner jqXHR as a new deffered object to return
我现在正在开发一个jQuery插件,我想在$.ajax
发送之前做一些预处理操作:
// The signature is the same with $.ajax
$.myAjax = function(url, options) {
var data = options.data;
var promises = [];
for(var name in data) {
if(data.hasOwnProerty(name)) {
var val = data[name];
if(val instanceof File) {
// I want to do some async pre-process here.
var dfd = $.Deferred();
var reader = new FileReader();
reader.onload = function(e) {
data.name = e.target.result;
dfd.resolve();
}
reader.readAsText(val);
promises.push(dfd.promise());
}
}
}
var deferred = $.Deferred();
$.when.apply($, promises).done(function() {
// In fact, I want to return, or wrap cascading this jqXHR
// in the outer function `$.myAjax`.
var jqXHR = $.ajax(url, options).done(function(...) {
// ??? If I want deferred to be a jqXHR like object,
// how to wrap the arguments here?
deferred.resolve(/* Help to fill */);
}).fail(function(...) {
deferred.reject(/* Help to fill */);
});
});
// ** ATTENTION **
// Here, I want to return a jqXHR compatible promise.
// That is what I ask here.
return deferred.promise();
}
我想在myAjax
中返回一个Deferred对象,或者更准确地说,一个jqXHR对象。
这样我就可以调用,完全相同的接口与标准$.ajax
方法:
$.fn.myAjax({...}).done(function(data, textStatus, jqXHR) {
// ...
}).fail(function(jqXHR, textStatus, errorThrown) {
// ...
})
// .always ... etc.
如果我正确理解了你想做的事情,那是不可能的。问题是,您的代码在创建jqXHR对象之前就从$.myAjax()
返回,因此jqXHR对象不可能是$.myAjax()
函数调用的实际返回对象。你可以从返回的承诺中访问它,但是返回的承诺将是你在ajax调用开始之前创建的承诺。
仅供参考,你的代码中也有一些承诺反模式,因为你从$.when()
处理程序返回$.ajax()
,而不是使用你创建的另一个延迟。从.then()
处理程序中返回一个承诺会自动将该承诺链接到原始承诺。
这是你发布的解决方案的清理版本。更改摘要:
- 将文件读取封装到一个局部函数中,以避免在循环中声明函数,并允许所有核心逻辑流只使用承诺而不是承诺和回调的混合(例如封装回调)。
- 为文件读取器添加错误处理
- 切换延迟回调模型(std承诺使用)
- 删除了延迟的反模式,而只是返回ajax承诺,它将根据需要为您提供ajax调用的解析或拒绝参数
- 切换到
.then()
,它具有更标准的行为,并且当jQuery使其承诺标准兼容时不需要更改
代码:
// The signature is the same with $.ajax
$.myAjax = function(url, options) {
function readFile(data, name) {
var file = data[name];
if (file instanceof File) {
return $.Deferred(function(dfd) {
var reader = new FileReader();
reader.onload = function(e) {
dfd.resolve(e.target.result);
data[name] = e.target.result;
};
reader.onerror = reader.onabort = dfd.reject;
reader.readAsText(file);
}).promise();
}
}
var data = options.data;
var promises = [];
for(var name in data) {
if(data.hasOwnProerty(name)) {
promises.push(readFile(data, name));
}
}
// trigger when all file fields was loaded.
// so the data were all constructed.
return $.when.apply($, promises).then(function() {
return $.ajax(url, options);
});
}
我自己的解决办法:
最后,我尝试制作一个延迟的resolveWith
或rejectWith
的jqXHR.done()
和jqXHR.fail()
签名的参数列表。
签名引用:http://api.jquery.com/jQuery.ajax/#jqXHR
jqXHR.done(function( data, textStatus, jqXHR ) {});
jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {});
jqXHR.always(function( data|jqXHR, textStatus, jqXHR|errorThrown ) {});
所以整体的解决方案是:
// The signature is the same with $.ajax
$.myAjax = function(url, options) {
var data = options.data;
var promises = [];
for(var name in data) {
if(data.hasOwnProerty(name)) {
var val = data[name];
if(val instanceof File) {
(function(name, val) {
// Deferred for a single field loaded.
var dfd = $.Deferred();
var reader = new FileReader();
reader.onload = function(e) {
data[name] = e.target.result;
dfd.resolve();
}
reader.readAsText(val);
promises.push(dfd.promise());
})(name, val);
}
}
}
// Overall deferred to cascading jqXHR from ajax.
// with returning the same argument list.
var deferred = $.Deferred();
// resolveWith or rejectWith requires a context.
// Thought from the jQuery ajax source code.
var callbackContext = options.context || options;
// trigger when all file fields was loaded.
// so the data were all constructed.
$.when.apply($, promises).done(function() {
// ********** FINAL SOLUTION **********
$.ajax(url, options).done(
function(data, textStatus, jqXHR) {
deferred.resolveWith(context,
[data, textStatus, jqXHR]);
}).fail(
function(jqXHR, textStatus, errorThrown) {
deferred.rejectWith(context,
[jqXHR, textStatus, errorThrown]);
});
});
// So that the resulting promise is well constructed.
return deferred.promise();
}
因此,现在我们可以使用$.myAjax
函数与$.ajax
相同的方法:
var dfd = $.myAjax({
url: '...',
// ...
}).done(function(data, textStatus, jqXHR) {
// triggered when the inner ajax done ...
}).fail(function(jqXHR, textStatus, errorThrown) {
// triggered when the inner ajax fail ...
});
相关文章:
- 获取最后一个返回值
- 使用谷歌地图:一个返回undefined的自定义javascript函数
- eval(“{}”) vs eval (“x={}”),一个返回 undefined,而另一个返回 {}
- 如何使用 jQuery 创建一个返回 SQL 结果集的函数
- JavaScriptAJAX调用一个返回XML的PHP
- 在什么情况下,您会使用一个返回另一个函数(Javascript)的函数
- 如何创建一个返回等待EventEmitter的promise的函数
- Angularjs模拟一个返回$http承诺的函数
- 如何编写一个返回单个值的java脚本函数,并将返回的值分配给我们想要的任何变量
- Jasmine-测试一个返回localStorage项的服务
- 创建一个返回函数
- 给定子域中的cookie,您可以编写一个返回代理服务器主机名的javascript函数吗
- 在Javascript中给Number添加一个返回变量
- 如何在angularjs中编写一个返回承诺的异步方法
- HTML4和HTML5 -如果没有导航,我怎么知道是否有一个“返回”状态
- 如何在JavaScript中创建一个返回本地文本文件值的函数?
- 如何声明一个javascript变量并将其值赋给一个返回布尔值的php文件
- 如何在异步测试中测试一个返回承诺的存根
- 从php传递一个返回值给js
- 输入一个返回的可观察对象