使用callback是能有效的达成异步不错,但是当业务变得庞大时,很多地方我们都要使用到异步调用。但如果异步调用嵌套多次,或者调用之间的依赖关系复杂的话,难免代码会变得混乱不堪。举个例子,假设我们需要依次调用服务A、B、C、D,我们就不得不把调用下一个接口的代码写在上一个接口的回调函数中。
这时,future/promise的异步调用方式就应运而生了,采用这种方式我们定义回调函数中完成当前回调逻辑后,返回一个future对象即可。future对象表示着这个回调逻辑已经完成,但是具体后续还要进行什么操作,我们都不关心。之后的调用都交给then(bind(&handlernext))来解决。所以他的代码看上去就要清晰明了的多了。下面是两种模式代码之间的对比:
采用callback模式下的异步多层调用:
//以下为伪代码,仅为了方便理解future/promise的概念
callbackA(...)
{
... //完成回调的业务逻辑
Proxy->async_doSomething(callbackB, param1, param2...); //异步调用B服务
}
callbackB(...)
{
... //完成回调的业务逻辑
Proxy->async_doSomething(callbackB, param1, param2...); //异步调用C服务
}
callbackC(...)
{
... //完成回调的业务逻辑
Proxy->async_doSomething(callbackB, param1, param2...); //异步调用D服务
}
callbackD(...)
{
... //完成回调的业务逻辑
}
main()
{
...
Proxy->async_doSomething(callbackA, param1, param2...); //异步调用A服务
...
}
采用future/promise的异步多层调用:
callbackA(...)
{
... //完成回调的业务逻辑
return Proxy->promise_async_doSomething(param1, param2...) //调用下一步的服务并将其传出
}
callbackB(...)
{
... //完成回调的业务逻辑
return Proxy->promise_async_doSomething(param1, param2...) //调用下一步的服务并将其传出
}
callbackC(...)
{
... //完成回调的业务逻辑
return Proxy->promise_async_doSomething(param1, param2...) //调用下一步的服务并将其传出
}
callbackD(...)
{
... //完成回调的业务逻辑
}
main()
{
...
Proxy->promise_async_doSomething(param1, param2...).then(bind(&callbackA)).then(bind(&callbackB)).then(bind(&callbackC)).then(bind(&callbackD));
...
}
看到上面的示例,我们发现,future/promise的代码更加的简洁,使得可读性更高。
这得益于promise_async_dosomething 和 then 的返回值都是future。所有回调函数都只需要调用接口并返回“已经调用接口”这一“事实”,而不关心下一步的回调函数在哪里,回调函数之间的调用关系都只通过Future.then()来连接。这样便可以很清楚地看出服务之间的调用关系是线性的。
串行异步
下面用一个简单例子来进一步学习future/promise的用法:有一个服务getBigger(int a,int b),能获取到2个int中的较大值。现在使用异步调用来获取到三个int中的最大值。
//#include
#include"servant/Communicator.h"
#include"servant/ServantProxy.h"
#include"servant/Application.h"
#include"GetBiggerServer.h"
using namespace std;
using namespace taf;
using namespace MTT;
bool flag = false;
//TODO:第一个异步调用,需要传入prx,原因是return的future去调下一个异步调用,需要proxy代理指针
promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > handler1(GetBiggerServerPrx prx,
const promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr>& f)
{
try
{
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult = f.get();
cout<<"the return value in first calling is "<< pResult->_ret <<endl;
cout<<"the bigger num in first calling is " << pResult->bigger <<endl;
return prx->promise_async_getBigger(pResult->bigger,100);
}
catch(exception& e)
cout<<"exception: "<<e.what()<<endl;
}
//TODO:第二个异步调用,这里不用传入prx,原因是跟100比完直接就返回了,只有要调用下一个异步时才需要proxy代理指针的传入,所以返回值也直接是int
int handler2(const promise::Future<GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr>& f)
{
try
{
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult = f.get();
cout<<"the return value in second calling is "<<pResult->_ret<<endl;
cout<<"the bigger num in second calling is "<<pResult->bigger<<endl;
flag = true;
return pResult->_ret;
}
catch(exception& e)
cout<<"exception: "<<e.what()<<endl;
}
int main(int argc, char* argv[])
{
if (argc != 3)
{
cout << "parameters is wrong!!" << endl;
return 1;
}
//taf::Communicator _comm;
CommunicatorPtr _comm = new Communicator();
int a, b;
a = atoi(argv[1]);
b = atoi(argv[2]);
//GetBiggerServerPrxCallbackPtr cb = new GetBiggerServerPrxCallbackImp();//
try
{
GetBiggerServerPrx prx;
_comm->stringToProxy("MTT.GetBiggerServerOfFerlan.GetBiggerServer@tcp -h 100.65.11.79 -p 10001 -t 60000", prx);
cout << "start" << endl;
prx->promise_async_getBigger(2, 10).then(bind(&handler1, prx)).then(&handler2);
while (!flag)
{
cout << "promise ansyc calling..." << endl;
sleep(1);
}
cout << "end" << endl;
}
catch (std::exception& e)
{
cerr << "std::exception:" << e.what() << std::endl;
}
catch (...)
{
cerr << "unknown exception." << std::endl;
}
return -1;
}
分步来看这段异步逻辑:prx->promise_async_getBigger(2, 10).then(bind(&handler1, prx)).then(&handler2)。
一, prx->promise_async_getBigger(2,10)
上面这部分是进行一个promise异步调用,中间的逻辑我们暂时不讨论,反正最后是返回了一个future对象,我们这里将这个结果记做A
promise::FutureGetBiggerServerProxy::promise_async_getBigger(taf::Int32 a,taf::Int32 b,const map& context)
{
//定义一个promise对象
promise::Promise< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr>promise;
//定义一个callback对象,将promise作为回调类传入。
//当异步调用成功时,通过callback对象,触发promise的setvalue对内部的m_future进行赋值。
GetBiggerServerPrxCallbackPromisePtr callback
= newGetBiggerServerPrxCallbackPromise(promise);
taf::JceOutputStream _os;
_os.write(a, 1);
_os.write(b, 2);
std::map _mStatus;
taf_invoke_async(taf::JCENORMAL,"getBigger", _os.getByteBuffer(), context, _mStatus, callback);
return promise.getFuture();//异步先返回promise的getFuture方法
}
二,A.then(bind(handler1,prx))
这样来思考:第一步进行异步调用的过程中,我们是定义了一个promise对象的,当taf_invoke_async完成后,会触发promise内部的setValue函数对其承诺的值进行赋值。所以我们要对setValue这个函数再进行注册一个回调函数,当setValue完成后,调用handler1进行回调。
那么首先bind(handler1,prx) 的作用就是将一个函数变成一个callback对象,内部逻辑我们不关心,这里只说几个bind函数需要注意的点:
1.回调函数的最后一个参数是一个Future,比如上例中,当 A 中的值被设置时,回调函数handler1将被调用,此时A将作为其最后一个参数传递进来,这样子handle函数中才能将设置的值给取出来.
2.使用bind将回调函数转换为Callback对象时,不允许传递非const引用的参数。因为Callback的Invoked分发函数接收原参数,保存的是原参数的一份拷贝。如果想传递非const引用,通过放在智能指针中的context来传递。
3.不允许传递原始指针作为参数,因为可能造成内存泄漏。
bind成功将handler1封装成一个callback类以后,这个类作为参数传入A.then( ),then()的目的就是注册回调,当setValue执行完毕,调用handler1。then内部也是使用了promise/future来设置回调的,具体逻辑我们不关心,只要知道最后then的返回值是一个future即可。
这里我们将A.then(bind(handler1,prx))的返回值暂记做B。
三,prx->promise_async_getBigger(2, 10)内部promise触发setValue后,调用handler1回调函数。
//TODO:第一个异步调用,需要传入prx,原因是return的future去调下一个异步调用,需要proxy代理指针
promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > handler1(GetBiggerServerPrx prx,
const promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr>& f)
{
try
{
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult = f.get();
cout<<"the return value in first calling is "<< pResult->_ret <bigger <promise_async_getBigger(pResult->bigger,100);
}
catch(exception& e)
cout<<"exception: "<
我们看到,handler1函数中将第一个异步调用的返回值和较大值都打印出来了,并且返回另一个异步调用。
四,B.then(bind(&handler2))
后面逻辑就一样了。prx->promise_async_getBigger(pResult->bigger,100) 在完成了setValue以后,会触发handler2函数,handler2函数中进行最后的输出,因为后续没有异步调用了,所以直接返回int即可。
我们再来顺一遍:
首先 prx->promise_async_getBigger(2,10)异步返回future,内部完成getBigger函数的调用后,触发setValue函数,setValue函数执行完毕,会调用之前注册的回调handler1,handler1的返回值是另一个异步调用prx->promise_async_getBigger(pResult->bigger,100) 。当完成getBigger函数调用后,触发setValue函数。setValue执行完毕后,会调用注册的回调handler2,handler2内部输出最终的结果。到此使用promise/future异步调用来获取3个int中的最大值就结束了。
并行异步:
上面是串行异步调用,下面来尝试一下并行的异步调用:还是使用getBigger服务,来获取4个int的最大值。
并行的异步调用由whenAll和Tuple来实现。Tuple是一个类似pair的模板,它可以有任意数量的成员,每个确定的tuple类型的成员数目是固定的。
whenAll接受多个future作为参数,返回一个future(通过Tuple组合)。内部逻辑是定义一个promise,然后对并行的几个future注册回调,当几个future满足条件时,就调用回调函数对promise中对应的部分进行设置,当所有future对应的部分都设置完成,该promise对应的future满足条件。
promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > f1 = prx->promise_async_getBigger(2,10);
promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > f2 = prx->promise_async_getBigger(1,100);
whenAll(f1,f2).then(bind(&handler3,prx)).then(bind(&handler2));
如上面的代码,我们先分别发起异步调用得到f1和f2两个future,然后使用whenAll并行的等待两个future满足条件,当f1,f2满足后,调用handler3.如下图
promise::Future handler3 (GetBiggerServerPrx prx,
const Future< Tuple< Future,Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > > > &f)
{
try
{
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult1 = f.get().get<0>().get();
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult2 = f.get().get<1>().get();
return prx->promise_async_getBigger(pResult1->bigger,pResult2->bigger);
}
catch (exception& e)
{
cout<< "exception: "<
这里handler3函数的最后一个参数是之前whenAll的返回值,所以我们看到他是一个Future
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult1 = f.get().get<0>().get();
第一层f.get()解开最外层的Future获取到内部的Tuple<>
第二层f.get().get<0>()解开Tuple<>并取得内部第一个具体的Future f1
底三层f.get().get<0>().get()解开最内层的Future获取到f1的内容。
分别取到f1,f2内容后,再调用promise_async_getBigger()进行获取最大值。后面就和前面所述完全一样了。
完整demo:
#include"servant/Communicator.h"
#include"servant/ServantProxy.h"
#include"servant/Application.h"
#include"GetBiggerServer.h"
#include "promise/when_all.h" //注意使用when_all和tuple要包对应的头文件!
#include "promise/promise.h"
#include "promise/tuple.h"
using namespace std;
using namespace taf;
using namespace promise;
using namespace MTT;
bool flag = false;
//TODO:第二个异步调用,这里不用传入prx,原因是跟100比完直接就返回了,只有要调用下一个异步时才需要proxy代理指针的传入
int handler2(const promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr>& f)
{
try
{
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult = f.get();
cout<<"the return value in second calling is "<_ret<bigger<_ret;
}
catch(exception& e)
{
cout<<"exception: "< handler3 (GetBiggerServerPrx prx,
const Future< Tuple< Future,Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > > > &f)
{
try
{
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult1 = f.get().get<0>().get();
GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr pResult2 = f.get().get<1>().get();
return prx->promise_async_getBigger(pResult1->bigger,pResult2->bigger);
}
catch (exception& e)
{
cout<< "exception: "<stringToProxy("MTT.GetBiggerServerOfFerlan.GetBiggerServer@tcp -h 100.65.11.79 -p 10001 -t 60000", prx);
cout << "start" << endl;
promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > f1 = prx->promise_async_getBigger(2,10);
promise::Future< GetBiggerServerPrxCallbackPromise::PromisegetBiggerPtr > f2 = prx->promise_async_getBigger(1,100);
whenAll(f1,f2).then(bind(&handler3,prx)).then(bind(&handler2));
while (!flag)
{
cout << "promise ansyc calling..." << endl;
sleep(1);
}
cout << "end" << endl;
}
catch (std::exception& e)
{
cerr << "std::exception:" << e.what() << std::endl;
}
catch (...)
{
cerr << "unknown exception." << std::endl;
}
return -1;
}