如何使用jQuery递延的功能,而不是async.waterfall? [英] How to use jQuery Deferred functionality instead of async.waterfall?

查看:132
本文介绍了如何使用jQuery递延的功能,而不是async.waterfall?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的函数调用链和async.waterfall使用。它的工作原理就像一个魅力。但我想这样做与jQuery推迟。如何将我的code?

从jQuery的网站的例子是这样的。这两种结果传递给完成功能:

  $。当($阿贾克斯(/page1.php),$。阿贾克斯(/page2.php)).done(功能(A1,A2){
  // A1和A2解决的第一页和第二页Ajax请求的参数,分别为。
  //每个参数是用以下结构的数组:[数据,状态文本,jqXHR]
  VAR数据= A1 [0] + A2 [0]; // A1 [0] =鞭,A2 [0] =它
  如果(/鞭/ .TEST(数据)){
    警报(我们得到了我们来抓!);
  }
});

但我的code是不同的。我需要一个回调传递给了瀑布的每一步,我有如果 S IN回调。如何用jQuery实现它?这可能吗?

  async.waterfall([
        功能(CB){
            VK.Api.call('users.get',{user_ids:res.session.mid,字段:田},功能(userDataRes){
                CB(NULL,userDataRes);
            });
        },
        功能(userDataRes,CB){
            如果(userDataRes.response [0]。市){
                VK.Api.call('database.getCitiesById',{city_ids:userDataRes.response [0]。城市},功能(cityDataRes){
                    CB(NULL,userDataRes,{城市:cityDataRes.response [0]。名称});
                });
            }
            其他{
                CB(NULL,userDataRes,{});
            }
        },
        功能(userDataRes,cityDataRes,CB){
            如果(userDataRes.response [0] .country){
                VK.Api.call(database.getCountriesById,{country_ids:userDataRes.response [0] .country},功能(countryDataRes){
                    CB(NULL,userDataRes,cityDataRes,{国家:countryDataRes.response [0]。名称});
                });
            }
            其他{
                CB(NULL,userDataRes,{},{});
            }
        },
        功能(userDataRes,cityDataRes,countryDataRes,CB){
            VAR resObj = $ .extend(真{} userDataRes.response [0],cityDataRes,countryDataRes);
            CB(NULL,resObj);
        },
    ]
    功能(ERR,RES){
        的console.log(RES :::,RES);
    }
);

UPD 1:

所以,我已经实现了一个解决方案,但预期它不工作。有一个在。然后()和jQuery递延流异步API函数调用有破损。我不知道如何做一个。然后()函数作为API的回调。

  VAR DFR = $ .Deferred();dfr.then(功能(VAL){    //这是一个异步API函数调用。而其回调返回结果传递到下一个。那么()
    //但是jQuery的递延流不遵循此API调用。
    //它一起去到下一个。那么忽略此API调用。
    //如何使它进入这个API调用,并从一个API的回调返回。
    VK.Api.call('users.get',{user_ids:res.session.mid,字段:田},功能(userDataRes){
        // CB(NULL,userDataRes);
        的console.log(countryDataRes:,userDataRes);
        返回userDataRes;
    });
})。
然后(功能(userDataRes){
    的console.log(countryDataRes:,userDataRes);
    如果(userDataRes.response [0]。市){
        VK.Api.call('database.getCitiesById',{city_ids:userDataRes.response [0]。城市},功能(cityDataRes){
            // CB(NULL,userDataRes,{城市:cityDataRes.response [0]。名称});
            返回[userDataRes,{城市:cityDataRes.response [0]。名称}];
        });
    }
    其他{
        // CB(NULL,userDataRes,{});
        返回[userDataRes,{}];
    }
})。
然后(功能(ARES){
    如果(ARES [0] .response [0] .country){
        VK.Api.call(database.getCountriesById,{country_ids:战神[0] .response [0] .country},功能(countryDataRes){
            // CB(NULL,userDataRes,cityDataRes,{国家:countryDataRes.response [0]。名称});
            返回[战神[0],战神[1],{国家:countryDataRes.response [0]。名称}];
        });
    }
    其他{
        CB(NULL,战神[0],{},{});
    }
})。
然后(功能(ARES){
    VAR resObj = $ .extend(真{}战神[0] .response [0],战神[1],战神[2]);
    的console.log(cityDataRes:战神[1]);
    的console.log(countryDataRes:ARES [2]);
    CB(NULL,resObj);
    返回resObj;
})。
完成(功能(RES){
    的console.log(RES :::,RES);
});dfr.resolve();


解决方案

让我们先从一般规则使用承诺:


  

每一个函数,做一些异步必须返回一个承诺


哪些功能这些都是你的情况?基本上,完整的瀑布,每年的花瀑布功能的 CB VK.Api.call

嗯, VK.Api.call 不返回的承诺,这是一个库函数,所以我们不能修改它。第2条进场:


  

创建凡不功能的即时包装


在我们的例子中,它看起来是这样的:

 函数callApi(方法,数据){
    变种DFR = $ .Deferred();
    VK.Api.call(方法,数据,功能(结果){
        dfr.resolve(结果);
    });
    //没有错误回调?那真可怕!
    //如果它提供的,调用`dfr.reject(ERR)`从中
    返回dfr.promise();
}

现在我们只承诺左右,也不再需要任何deferreds。第三条规则发挥作用的:


  

文件做一些事与异步结果一切都进入一个。然后回调


  
  

...并返回其结果。


这结果很可能会成为一个承诺不是一个普通值,。然后可以处理这些 - 并将给我们回用于执行的最终结果的新承诺东西。所以,让我们有些链则()取值:

  apiCall('users.get',{user_ids:res.session.mid,字段:域})
。然后(功能(userDataRes){
    的console.log(countryDataRes:,userDataRes);
    如果(userDataRes.response [0]。市){
        返回apiCall('database.getCitiesById',{city_ids:userDataRes.response [0]。市})
        。然后(功能(cityDataRes){
            返回[userDataRes,{城市:cityDataRes.response [0]。名称}];
        });
    }其他{
        返回[userDataRes,{}];
    }
})
。然后(功能(ARES){
    如果(ARES [0] .response [0] .country){
        返回apiCall(database.getCountriesById,{country_ids:战神[0] .response [0] .country})
        。然后(功能(countryDataRes){
            返回[战神[0],战神[1],{国家:countryDataRes.response [0]。名称}];
        });
    }其他{
        返回[战神[0],战神[1],{}];
    }
})
。然后(功能(ARES){
    VAR resObj = $ .extend(真{}战神[0] .response [0],战神[1],战神[2]);
    的console.log(cityDataRes:战神[1]);
    的console.log(countryDataRes:ARES [2]);
    返回resObj;
})
.done(功能(RES){
    的console.log(RES :::,RES);
});


至少,这是你原来的瀑布做了什么。让我们通过执行 getCitiesById getCountriesById 并行和删除明确创建这些所有的样板,把它擦亮了一下战神数组。

 函数callApi(方法,数据){
    变种DFR = $ .Deferred();
    VK.Api.call(方法,数据,功能(结果){
        dfr.resolve(result.response [0]);
//改变:^^^^^^^^^^^^
    });
    //没有错误回调?那真可怕!
    //如果它提供的,调用`dfr.reject(ERR)`从中
    返回dfr.promise();
}
apiCall('users.get',{user_ids:res.session.mid,字段:域})
。然后(功能(用户数据){
    如果(userData.city)
        变种cityProm = apiCall('database.getCitiesById',{city_ids:userData.city});
    如果(userData.country)
        变种countryProm = apiCall(database.getCountriesById,{country_ids:userData.country});
    返回$。当(cityProm,countrProm)。然后(函数(城市,国家){
        VAR resObj = $ .extend(真{}用户数据);
        如果(市)
            resObj.city = city.name;
        如果(国家)
            resObj.country = country.name;
        返回resObj;
    });
})
.done(功能(RES){
    的console.log(RES :::,RES);
});

I have a chain of function calls and use async.waterfall. It works like a charm. But I'd like to do it with jQuery Deferred. How to transform my code?

The example from jQuery site is like this. Both results are passed to done function:

$.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!" );
  }
});

But my code is different. I need to pass a callback to every step of a waterfall and I have ifs in callbacks. How to implement it with jQuery? Is it possible?

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:

So, I've implemented a solution, but it doesn't work as expected. There is an asynchronous API function call in .then() and jQuery deferred flow is broken there. I don't know how to make a .then() function as an API callback.

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();

解决方案

Let's start with the general rule for using promises:

Every function that does something asynchronous must return a promise

Which functions are these in your case? Basically, the complete waterfall, each of the waterfall functions that took a cb and VK.Api.call.

Hm, VK.Api.call doesn't return a promise, and it's a library function so we cannot modify it. Rule 2 comes into play:

Create an immediate wrapper for every function that doesn't

In our case, it will look like this:

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();
}

Now we have only promises around, and do no more need any deferreds. Third rule comes into play:

Everything that does something with an async result goes into a .then callback

…and returns its result.

That result might as well be a promise not a plain value, .then can handle these - and will give us back a new promise for the eventual result of executing the "something". So, let's chain some 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);
});


At least, that's what your original waterfall did. Let's polish it up a bit by executing getCitiesById and getCountriesById in parallel, and removing all the boilerplate of explicitly creating these aRes arrays.

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);
});

这篇关于如何使用jQuery递延的功能,而不是async.waterfall?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆