我有一系列函数调用并使用async.waterfall。它就像一个魅力。但我想用jQuery Deferred做这件事。如何转换我的代码?
jQuery网站的例子是这样的。两个结果都传递给done
函数:
$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) ).done(function( a1, a2 ) {
// a1 and a2 are arguments resolved for the page1 and page2 ajax requests, respectively.
// Each argument is an array with the following structure: [ data, statusText, jqXHR ]
var data = a1[ 0 ] + a2[ 0 ]; // a1[ 0 ] = "Whip", a2[ 0 ] = " It"
if ( /Whip It/.test( data ) ) {
alert( "We got what we came for!" );
}
});
但我的代码不同。我需要将回调传递给waterfall
的每一步,并且我在回调中有if
秒。如何用jQuery实现它?有可能吗?
async.waterfall([
function(cb) {
VK.Api.call('users.get', {user_ids: res.session.mid, fields: fields}, function(userDataRes) {
cb(null, userDataRes);
});
},
function(userDataRes, cb) {
if(userDataRes.response[0].city) {
VK.Api.call('database.getCitiesById', {city_ids: userDataRes.response[0].city}, function(cityDataRes) {
cb(null, userDataRes, {city: cityDataRes.response[0].name});
});
}
else {
cb(null, userDataRes, {});
}
},
function(userDataRes, cityDataRes, cb) {
if(userDataRes.response[0].country) {
VK.Api.call("database.getCountriesById", {country_ids: userDataRes.response[0].country}, function(countryDataRes) {
cb(null, userDataRes, cityDataRes, {country: countryDataRes.response[0].name});
});
}
else {
cb(null, userDataRes, {}, {});
}
},
function(userDataRes, cityDataRes, countryDataRes, cb) {
var resObj = $.extend(true, {}, userDataRes.response[0], cityDataRes, countryDataRes);
cb(null, resObj);
},
],
function(err, res) {
console.log("res::: ", res);
}
);
UPD 1:
所以,我已经实施了一个解决方案,但它没有按预期工作。在.then()
中有一个异步API函数调用,jQuery延迟流程在那里被打破。我不知道如何将.then()
函数作为API回调。
var dfr = $.Deferred();
dfr.then(function(val) {
// THIS is an asynchronous API function call. And its callback returns result that is passed to the next .then()
// But jQuery deferred flow doesn't follow this API call.
// It goes along to the next .then ignoring this API call.
// How to make it enter this API call and be returned from a API's callback.
VK.Api.call('users.get', {user_ids: res.session.mid, fields: fields}, function(userDataRes) {
// cb(null, userDataRes);
console.log("countryDataRes: ", userDataRes);
return userDataRes;
});
}).
then(function(userDataRes) {
console.log("countryDataRes: ", userDataRes);
if(userDataRes.response[0].city) {
VK.Api.call('database.getCitiesById', {city_ids: userDataRes.response[0].city}, function(cityDataRes) {
// cb(null, userDataRes, {city: cityDataRes.response[0].name});
return [userDataRes, {city: cityDataRes.response[0].name}];
});
}
else {
// cb(null, userDataRes, {});
return [userDataRes, {}];
}
}).
then(function(aRes) {
if(aRes[0].response[0].country) {
VK.Api.call("database.getCountriesById", {country_ids: aRes[0].response[0].country}, function(countryDataRes) {
// cb(null, userDataRes, cityDataRes, {country: countryDataRes.response[0].name});
return [aRes[0], aRes[1], {country: countryDataRes.response[0].name}];
});
}
else {
cb(null, aRes[0], {}, {});
}
}).
then(function(aRes) {
var resObj = $.extend(true, {}, aRes[0].response[0], aRes[1], aRes[2]);
console.log("cityDataRes: ", aRes[1]);
console.log("countryDataRes: ", aRes[2]);
cb(null, resObj);
return resObj;
}).
done(function(res) {
console.log("res::: ", res);
});
dfr.resolve();
答案 0 :(得分:5)
让我们从使用承诺的一般规则开始:
每个执行异步操作的函数都必须返回一个承诺
您的案例中有哪些功能?基本上,完整的瀑布,每个瀑布函数都采用了cb
和VK.Api.call
。
嗯,VK.Api.call
没有返回承诺,而且它是一个库函数,因此我们无法对其进行修改。规则2发挥作用:
Create an immediate wrapper代表的所有功能
在我们的例子中,它看起来像这样:
function callApi(method, data) {
var dfr = $.Deferred();
VK.Api.call(method, data, function(result) {
dfr.resolve(result);
});
// No error callbacks? That's scary!
// If it does offer one, call `dfr.reject(err)` from it
return dfr.promise();
}
现在我们只有承诺,不再需要任何延期。第三条规则发挥作用:
使用异步结果执行操作的所有内容都会转入
.then
回调...并返回其结果。
这个结果也许是一个承诺而不是一个普通的价值,.then
可以处理这些 - 并且会给我们一个新的承诺,以便最终执行"某些事情"。所以,让我们链接一些then()
s:
apiCall('users.get', {user_ids: res.session.mid, fields: fields})
.then(function(userDataRes) {
console.log("countryDataRes: ", userDataRes);
if (userDataRes.response[0].city) {
return apiCall('database.getCitiesById', {city_ids: userDataRes.response[0].city})
.then(function(cityDataRes) {
return [userDataRes, {city: cityDataRes.response[0].name}];
});
} else {
return [userDataRes, {}];
}
})
.then(function(aRes) {
if (aRes[0].response[0].country) {
return apiCall("database.getCountriesById", {country_ids: aRes[0].response[0].country})
.then(function(countryDataRes) {
return [aRes[0], aRes[1], {country: countryDataRes.response[0].name}];
});
} else {
return [aRes[0], aRes[1], {}];
}
})
.then(function(aRes) {
var resObj = $.extend(true, {}, aRes[0].response[0], aRes[1], aRes[2]);
console.log("cityDataRes: ", aRes[1]);
console.log("countryDataRes: ", aRes[2]);
return resObj;
})
.done(function(res) {
console.log("res::: ", res);
});
至少,那是你原来的瀑布所做的。通过并行执行getCitiesById
和getCountriesById
并删除显式创建这些aRes
数组的所有样板,让我们稍微改善一下。
function callApi(method, data) {
var dfr = $.Deferred();
VK.Api.call(method, data, function(result) {
dfr.resolve(result.response[0]);
// changed: ^^^^^^^^^^^^
});
// No error callbacks? That's scary!
// If it does offer one, call `dfr.reject(err)` from it
return dfr.promise();
}
apiCall('users.get', {user_ids: res.session.mid, fields: fields})
.then(function(userData) {
if (userData.city)
var cityProm = apiCall('database.getCitiesById', {city_ids: userData.city});
if (userData.country)
var countryProm = apiCall("database.getCountriesById", {country_ids: userData.country});
return $.when(cityProm, countrProm).then(function(city, country) {
var resObj = $.extend(true, {}, userData);
if (city)
resObj.city = city.name;
if (country)
resObj.country = country.name;
return resObj;
});
})
.done(function(res) {
console.log("res::: ", res);
});