diff --git a/Implab.Test/AsyncTests.cs b/Implab.Test/AsyncTests.cs --- a/Implab.Test/AsyncTests.cs +++ b/Implab.Test/AsyncTests.cs @@ -328,6 +328,59 @@ namespace Implab.Test { Assert.IsTrue(flags[1]); Assert.IsTrue(flags[2]); } + + [TestMethod] + public void ChainedCancel1Test() { + // + // OperationCanceledException + var p = PromiseHelper + .Sleep(1, "Hi, HAL!") + .Chain(x => { + // + var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!"); + // + PromiseHelper + .Sleep(100, "HAL, STOP!") + .Then(() => result.Cancel()); + return result; + }); + try { + p.Join(); + } catch (TargetInvocationException err) { + Assert.IsTrue(err.InnerException is OperationCanceledException); + } + } + + [TestMethod] + public void ChainedCancel2Test() { + // , + IPromiseBase p = null; + var pSurvive = new Promise(); + var hemStarted = new ManualResetEvent(false); + p = PromiseHelper + .Sleep(1, "Hi, HAL!") + .Chain(x => { + hemStarted.Set(); + // + var result = PromiseHelper + .Sleep(1000, "HEM ENABLED!!!") + .Then(s => pSurvive.Resolve(false)); + + result + .Cancelled(() => pSurvive.Resolve(true)); + + return result; + }); + + hemStarted.WaitOne(); + p.Cancel(); + + try { + p.Join(); + } catch (OperationCanceledException) { + Assert.IsTrue(pSurvive.Join()); + } + } } } diff --git a/Implab.suo b/Implab.suo index 8db46086f47396d82a71e4463e1728188d310d43..7d0dced8f9843cdcbf2e228d319a32e165d49e3c GIT binary patch literal 71168 zc%1D$3z$>IxwF8+f~!1=_^vA#t>Pvmn{1M0rLdc9V09N3vMgL&gp=&a63A|1l3iFt zu|BW0J}T8(TLs0}tybzAUsp;Mq2;2s($>~mYwOi(TZ&hq)QWrmf6kfYBss}BCoeXT z+3(-;$ej8AdHwVM|IGaJ#_QwXyZ7-^|40(T*U515<=|*CQu1yXKpA@&Cp-@a_;PS? zkiEVXV6YU5CL9JB0d;m7KsmrD0Ic)T02Kho1B?MU4xmz~c5(v1Sb&oO#sf?M_zJ*8 zfD-{G0h|PI3c#rV1^^Sl>0(>?DqPP5r~)_(U^2kJ0Zain8{iy(uK}D3a2~)^fb#*W z0dxR*fN20V07d{aKplV`pccRaUgXf3(y4M z1egsl2cQ|?LV$|^<^i+kECaYie778~ zHvqWB=k^r+#s}|L0;~e?1M~p&0t5hp01W_#v#9?CkWzp|pe~vq-3SSh0iq>Lf>y)Q znxYW5YW?QZ9qq4|AQX29-+yC$+mBmQA=<)-$#apJWdYAp<<+d_PB zjX>Ro2@f=}_hE%}rjTJ}*blVeM;=-c#qWc9Y@;~CA`Lv0g$MpS0Q#YY8))m74^sg@ z_K&ZLG^oLke49nYwugXqf_$J{^6nP#UfTbWAF%$9WytzJ#+xYl7W+Sz3*!OEU$Fn5 z0idP-*NbV&^?&T6*#9q-;H?$!S^sD65#;(m_8;T{$UBe+u>OznS^vMB2>qX>a|IE2 z0p7Fzk36JZyhk3eNW5n};9?Tv1;_)IiO@j@+5ebc}CHHH^`*o6cH^cpv0N;IZ>VfS#&)jFG?zVsHp_UI7nod&KZy&AollXw0 z0UIt8EGn^eUeXbE@b13uwh$lYdm~)L7wB!{Jgc~FzPhS|4~5}*hS8v>f7Mm?K7XVy z#LwvE`ywIEUtQJK=l1(N7x4q_fmM9(47b7Lt@T>HMq^j4firQV${1!0Bl=o>o=_kh z@J93myZ`uO^{~}*+S73Em@8BX_yDu2lxA}T|Ik%su4^3+# zd~ew2_W6C00eu@%8iqF>{`?kt!-w!YS0oVPF`vi9YU-S_9DfaZE}_$GO1&aNI`zr4~QRI4g$9N z@SiQFsTO|)iShqL{7#zZE|5tA@HRwgQNoXSD>UH89y?de{|Gf7s0f(Om4vlJVN~yi z{~SmxRWw(A+M=qU#?}C5wt!^U3DT4gau}j*GXUd1${`(e6zrqzw+hN30{30CE~@BQ zs0ZkTSa$f2-$vjXrX__k*3AW{0sD&|FnB2ij1u-dbSM(u4h4#QRKD@k_&lX3yD%)r z@3e$$`8Uc>txytv%5Sg~IohWCs8lG(#wfK6r?Km)u5%gGQ!V@%;A#Z0fmCDzjmHeN zrGx(sz*P)zR|n5Vc*b8bz7y{40F4mW2{CFxvPsQ<2B5y&fZv~%{&Od(@YGGmUoQHKe5lT#{iA*4 zzEQ&cJZOC@q17V~PORe&s==fWlOg}uo>2!=iyv(&jPL28tXC)Soq1IHVO(c$u%2pr z^1MlCofx-*>TS8?P4!ee%Vmmskd=Abzc|{q*1^}kz-g)=W*f9OJ=A<-R03{3#p;+>~O`etUzG{{I>u# z7D|g!ONe2$01hX7k0}`_2Fz@YF|JKbsE<(SDU$ z1~+7x|9MpQ7y1!Gx1{Ac;7v#&txVb<+V!8BB-|%*Keg?ih0`;@kMpMIiuZ~0UoN07 zK*xO#Eg2R4Uo(E33ugMSlS%;w;HXWa9ATn76J;nPm5)qhS+tcTm(KJI@Z%iRxOrOe znqe5E9e=$Ee)~UshIbe2-*qDJcd`-0fb2ECX`OdYAg`LXn)cI+v4Qg1Faf?P`B z*=hteke&%JH^DP%n>x6wq522XVgxE`sdj?DIjGcbr7`QMc2WZ|YUq9j(=WnwwuNaa zD|3Js^@Q?28>e-@e|y8GrHf{5*gk<;22ngq)E`pmDJ&%FB}(xz9=n&~k)uZ%r17pQ z%XH>dD1#+x8J$~9IwU5~iv`HO#3b9OFyR*!Aj9kt{cHEVvw7h!K7RD!-8;LH!4D<` zGD{nN9EN+SsKAR@HoSk0|Eb>1{aenwwf_40vCkXjut@bD)Oy0u!ci+}C(E_t7u386 zRae4da$!2ixarWsL`Q+XF~=D;JTZHG;E`7sFPUJyCooK&KDoZf0yZ2koul#7an?ck z_(Hn=q%+(4Yk)WSZh#8E95uHy6?Q`1r#I|sxn=Di?Aw03V?>~Nf*kE($p^wbbc^cI z(@hpu0T_DvT&_Q}mZ+BQ;-c0Xy)7Jn<0+kB`To`QgT+l-2< zw;fnvnR&ba;DtAS+C54Rw}5V^*ninv118xg-ecy1@W=F^PO2GsC;^>Oxuz4wj2b%5 zPl+QJc{CC_C2B9wQ`yo)N1Z8TE~Mq4MaR)D)+iy?a3ui4Lex=z=0)8z9L6)j1ex# zw)|Xls!13O1Pc0ht=;2%{)s1kxOdRMJhJgFxr&S;%tiaPQfsyO2)EzAPtx~%@V$*+ zZh7+dvmY7p!c1wlkqf)yo*6&>S(f={8jVy=s04m7yU}HzqCyYB@)Kl4EBx19g44CM z9~WG`Q7(7wgqSy zf0*gFfwBtGETp0TC7NDSaeyCh+;n;4E$?o>eVb+d+nkaxG@w~(j*R(v_nnoW zKk)qb?lZYQw{L8-?3W#bR%y?_l#9)y%rZoOABese@IQW!;n1|*s}0ZI|AKS$&PkhP zTZf-?K-6}zGI6!X8favgWIwG>Wh)fgAIB#2RHcAx^_kTn-u~yHSr8Z^uV{P2P(_B7 z;hNuZBs2T7lKrJdA+0Im`Ufrfzl@Nm6{%gcu{fjF*O;|4e%^uE}~$hZ`Ti zs&(rI>&q2Hw%^4goSWJBzfcr6*c+or!(SiT>z;V<_^QC>7vHRT>5}7RdyO=t)0>ku zW|E}!hqDw}_GCH!x!~C&_QHNT7lB`*<)s@mS6*>92)2J*lh4#DHSJGJ|1&A$r&dQT z?H|`mU#k+oSs8yB?qSdq_4&trLyWU3+kewrfX~T*{~%vwbK2hkxQYmMa*cSsPQ3ny zc>N|^ZxruuBC$OL|APB(1Kb9%0pL3Tw*%Y(@LhmA0lp{3|2|xQ0B|?JJpda4HUZoV za38>CfcwR`55o07iNK@q`G;_Q4B%0KABpd_!1c!fj{`gf@TB<0L6G43zniWA>&0{lXJ_m+75 z6T1ArSbJ_7h(fR6$G0`OOW zzlmx59j^Pu`~QRMr{etqxE>Vm55e`H0H2G`IOld4?}@no2bdC?^oNxRzn8+X7aHxZ zwnRJWXJZBZ2lq;4XWdLcx+RWv9-5LM502k;>(8gRzb-rDl-gR(LOP3`Fw<2JeAP;4 z`q-Ty=?xB|pdJ+U`=DrHmj)C|kYBsS`K;IoiQE^h6al)Xvr|H6Von9UFkOnN_GQd& zY-w)Jd%b8k^dMc10<@Rdjs>3DjZlY1G15!T!66AX4SA=g6|NYwTe5N_-Z+kQIFCqe zr83JRMbisFk$hRicB*A#ugYYtmOg2kpaF11=zO%B%3yp{bw^4Y3(%I?+KYJ2&Y#eZ zXCIOFliE&Kn7*a1Y%VNNOV&2!RG1R#pt1U>o*-{CM>_0BfG5wPe6cI3w(1K;OETw~ zSzw+Q)8b+@So2Opyn(nd`EI7`q6Cixe(|k8j)wfp1!^Y-3soZVZK08H39WI?5t0c~ zPc;{cs(4PJH0$SRg7FV1TOOrM?T%HnV0kHJ%$r740@r4|Jb747(PBCOC(m9I;RG+)WNVQsItbA;Ng^u1J{ z#vI1LWk*{;;u@;oR0_6$d`qEHOSk;{c>BN)KB_qDwfVWQ4^$GVeV`ujEr74=)Dw(m zTS3Bh^7OU>+%v3fD~RPkJFCIG0@h%rQggnwE_F*lkdmf?mll&2q?5_Tra3d7iL(Ws z0(qFQ)3WQKR9k5&V{b;xeIl>*sOG-(fpah*5bDlVvm|VK4M$4m5 zuF1cw@22x)%GOn+3cZM3M(W_Nk*@T1(7mX*0^d$|?&2D7C*|n4JGX)ES=G^<+8DC| z{&0_IBmJsQoHHv)Z$mF%sin7}(;iV)kiI7gWnbc%@;nn)s}v4vqSSP>^mLSRCoW zLsWMLxD3uD$*fTee);L~L&tTWy7!GOCrx_#i8F`szd0zk?mmtN zsdT1s|Esp?zu&EzTmHN0Tipv!88!J)(;45C+e4G3)Td$pfSM&2UmRZk@qrfIrc18g zx$+Uce@3}n9ZC`6NB$FDaIIhY^xtoD?%V&5YuA5OZp&dRc{LSB5qQ>E@ha6GSPpGXF!$`A@$C1@n(*SC}>6e+1xq@xBA_wE?CDP&;i@ zAHN9x&xSWH3NGl^^XSPm@?mNQ{)h1F8kYa0^A|?o%_n89fpCQH(c1%lKkvcg7sC44 zczl7+qj&h|3obOUyyoJI#fYv*$k*FlUDXrz1VVnFJ9bQiS@kImRx4NQskIpEOlIC- zt6N%qu?!V+xbTVyp0|)&TuX7Z9VMnyVGr~KxeyJyB1cnigbxKnzA%qRafJ1DKNk)= z_^_`VvXzUIK`OBsGz&$MgPfORu39t6jNa~OS`!R}A`Tzt4|F#M*0}gc1ZoB)(MiD& z-|q|bh5ZBe6})E^-_;EChR3njEddWbGUaskqPd6b=AD614;R6h&Anaxnu&paJ{0nG z@l9)dVNBWS^YhsIr^&P*SCIDrAhoQb>uQ17E$@KFd(g1)-Tj7kXlhOdX$7Wj$;qU zsgo+lkcUU3o%QK5$8-byU1l*k>uT%4bn47DyFu4rvRZXEhsA0)*)0~M!*Kb! z=ANLRbL-praHLamulIz5p}@*@p6Q*d1EE!&LC){zx;Xu+Ksd;SJNY%xVtP4$xYI|! zfr}>jRe4c$aKkM<`d8juI{r5P<3E1?dcCa286gN(Xi9nnv%!T2dOdg-hFq*$cUgnQ zZZ(@5Y&t`exj|=k+ATVp&C;MVSj;sxlcCmZv6?U6%!TeQD<>**daa>Xf2)`%h_Gp{2AD1HE zv6xD$0uwzzzkiCbDzGpsrc&>PivKmN+k zv+RsrI}y%f#g#3r6cq56&4=sxBs2e$iTGy|;T)`B93`p4xmlh+m5AR(Yd4}2d-mrq ztL^ixltcH*^!CE6(pKyC7%6fUoFb*Nk{`>D?ls)SV<(B%3uE3|Aaj^U-Tr zIQrM{51sjo*c$iTow{9o97@|7*L-`$W4n};>rlFhpgqz$$TGXaU9CL^%@jssNGmxt}_m4EV8MR%^g7wSU^;oDE`B0rf`QL>g=3lw~Eosxrr2ZcwvHEWmc|M*()tiuGrM*`e z@cgZlh%m#Z7C-u*A0yr?<-blLEq4Cacn$c`qPk2aewzyX7^g}D{vg3~qtxNAQMSLA z;oK`bc2^DltBG(18HUnIpjhsXE^Vu{5~#EiNb5?V;*n_6+w75}$;U{y@~6q}YAebA ziRa%b%O7Y!BAok%K^ecY{C^SAcK+X9BAidB4u5(H1oMxcmCqyU^S_U1 z$-h+zKb3#lG~oY)2f*y0y<%;gob90*Z6DGE&lL_wr9V>Z*TXLrYIEM1pL!}H2k3R2z8AZo)F+)dc zpKq4?Y~^#fyiAe*q~|`u`Kwy>PaHS<#CzrSM~eKn3@Zk$RBG`*p#i^1h5r-8J5vMx z#U!@=Nm>6>9)G@01f5zf{ufA0{};o!ZI=R{YKT7jX2FQwsB^UojlIiivlweUR$X14 zrAB9V+f2H;+O9fXjm2g#Te`Te8WVSUEPAc6j<<298lBNoSF1B~<{F4@@#<G?Ot+;{6M zYX|7?pS@@PWzW$S-RztNj{@2@8zHF zD5xi|zhT!~uS{MM_cc@cV6p;{cqhU zb(Vk>T_$->&%Ok!Ct99y#^6ZkFF!96JdfcOS0Fb_N>;i)ATc~e66d)S)3?*T%Ge{$ zmE7}GV;5GB$GjC^0DZZdRKfqbRR8}v+4 zMQ=_1Z)u8AZ9-p)wa9;n$g?CK6c3I0s>&rg_GL_b)#qSZ6 z^RO>iv3BQRCtG3tYqPG16vq437Ho1IagvfMJCQVCq2Pp?BWu@fv{CQKQh&kn#Wg~8 z1u9+nU6f(+z}gL!H=cKR=kNzk9lYY1#havV$FSI>e#@ouL$>B@5J zeSfJ;;VUp5{|*&i?VR%619zUi`#MX7DbD^diD=uIuWc_YA?Ul~XE~5StkjMtXWz-z_2}*#jNi3% zm7{G4oIbMP_%|EhthIjYqsYRu7nQGiCOs!Y4LqZHxBBSwTGD|WM!+PJ9n@~*Fvly&9$0ugtl)~6`$pB zY(~6a?P+s@pQSxHD8Zq_U4>IZ^4~JkO}P;|pQKF+NPlb-AtzCpB-@^xj&D}wNDe4u zTS%9muN9yjq!;}OTyX@?{`f3HPRVFYQgg~9A?Sc#i$^Inhg1o3R!`)hJr#E5kWgFr zPL1+;e&y$4m!C`-w%wBbG;>Zb?^W+CjpLHi3BSS~D=j|s)UK7q#gNOhE(Il7VG6(- zMb~?cD-XD-=KO7@eI{iI|+$*=w7 z=vZ+>$YhP0Ld7oJjhMu^P0K?TO z-j_o0ha>NmMb3{R?hDt_7nzsA1+*vfqQsKe`_*|yG-<+}=;ts!1)2Fe)aGNN#*eOFqMbhlCP_hud9-;tCFv)V&Z7I_H|V{6;An7xEVyd6qw61-iqf8En`kgD)l)- z)ANicbzNxFMzo(-WG+ZsW7z7l#Foj*{nANCLnBw(PdX~;o~4tHVmZx(*AJz0VwM+8 zO{qPp>3mH!QaX1jm)4Ww9S2IM5XEv=+;$#0>v_I-f|BLD5TirMYF@IMA8Ec{vYMBy z=Eaq}lGVIqH7{Asb8|vO{!%^?rKs%xRr&n0*#uXFs`o#GXK5W2@0IbZ^Z&fzoP#o&Hjgw)`#O?`DVpECcFK@R()1d^!%=HJ(gFO z7HXB|v9)%-v~K;#uUjwm>Y=p$_t;r`t#waqG6t+se1~=ryrT6ZR=;NxT;HuGoz(lk z^R-($HQ%ewupgE7|L0@>f8pz2+5e=n|8I0a*htjNzqr2W8{)k(er5R^-KjNbz`um3 e_djmR!}+_*iEsw18vJIJ=MQPY&y$${@&5-Em#`fG diff --git a/Implab/IPromise.cs b/Implab/IPromise.cs --- a/Implab/IPromise.cs +++ b/Implab/IPromise.cs @@ -8,14 +8,13 @@ namespace Implab public interface IPromise: IPromiseBase { - T Join(); - - T Join(int timeout); + new T Join(); + new T Join(int timeout); IPromise Then(ResultHandler success, ErrorHandler error); IPromise Then(ResultHandler success, ErrorHandler error); IPromise Then(ResultHandler success); - IPromise Error(ErrorHandler error); + new IPromise Error(ErrorHandler error); IPromise Error(ErrorHandler error); IPromise Map(ResultMapper mapper, ErrorHandler error); @@ -24,9 +23,9 @@ namespace Implab IPromise Chain(ChainedOperation chained, ErrorHandler error); IPromise Chain(ChainedOperation chained); - IPromise Cancelled(Action handler); - IPromise Finally(Action handler); - IPromise Anyway(Action handler); + new IPromise Cancelled(Action handler); + new IPromise Finally(Action handler); + new IPromise Anyway(Action handler); } } diff --git a/Implab/IPromiseBase.cs b/Implab/IPromiseBase.cs --- a/Implab/IPromiseBase.cs +++ b/Implab/IPromiseBase.cs @@ -23,8 +23,15 @@ namespace Implab { IPromiseBase Then(Action success,ErrorHandler error); IPromiseBase Then(Action success); + IPromiseBase Error(ErrorHandler error); + IPromiseBase Anyway(Action handler); + IPromiseBase Finally(Action handler); + IPromiseBase Cancelled(Action handler); IPromise Cast(); + void Join(); + void Join(int timeout); + } } diff --git a/Implab/Promise.cs b/Implab/Promise.cs --- a/Implab/Promise.cs +++ b/Implab/Promise.cs @@ -11,7 +11,7 @@ namespace Implab { public delegate T ErrorHandler(Exception e); public delegate void ResultHandler(T result); public delegate TNew ResultMapper(TSrc result); - public delegate IPromise ChainedOperation(TSrc result); + public delegate IPromise ChainedOperation(TSrc result); /// /// Класс для асинхронного получения результатов. Так называемое "обещание". @@ -86,7 +86,6 @@ namespace Implab { const int RejectedState = 3; const int CancelledState = 4; - readonly IPromiseBase m_parent; readonly bool m_cancellable; int m_childrenCount = 0; @@ -102,12 +101,15 @@ namespace Implab { public Promise(IPromiseBase parent, bool cancellable) { m_cancellable = cancellable; - m_parent = parent; - } - - void InternalCancel() { - // don't try to cancel parent :) - Cancel(false); + if (parent != null) + AddHandler( + null, + null, + () => { + if (parent.IsExclusive) + parent.Cancel(); + } + ); } bool BeginTransit() { @@ -159,6 +161,16 @@ namespace Implab { } /// + /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения. + /// + /// + /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение. + /// + public void Resolve() { + Resolve(default(T)); + } + + /// /// Выполняет обещание, сообщая об ошибке /// /// @@ -185,7 +197,18 @@ namespace Implab { /// /// true Операция была отменена, обработчики не будут вызваны.false отмена не возможна, поскольку обещание уже выполнено и обработчики отработали. public bool Cancel() { - return Cancel(true); + if (BeginTransit()) { + CompleteTransit(CancelledState); + OnStateChanged(); + return true; + } else { + return false; + } + } + + // сделано для возвращаемого типа void + protected void InternalCancel() { + Cancel(); } /// @@ -229,13 +252,11 @@ namespace Implab { return medium; } - public IPromiseBase Then(Action success,ErrorHandler error) - { + public IPromiseBase Then(Action success, ErrorHandler error) { return Then(x => success(), error); } - public IPromiseBase Then(Action success) - { + public IPromiseBase Then(Action success) { return Then(x => success()); } @@ -267,7 +288,7 @@ namespace Implab { errorHandler = x => { try { medium.Resolve(error(x)); - } catch(Exception e) { + } catch (Exception e) { medium.Reject(e); } }; @@ -338,7 +359,7 @@ namespace Implab { if (handler == null) return this; - var medium = new Promise(); + var medium = new Promise(this,true); AddHandler( x => { @@ -377,7 +398,7 @@ namespace Implab { throw new ArgumentNullException("mapper"); // создаем прицепленное обещание - var chained = new Promise(); + var chained = new Promise(this,true); ResultHandler resultHandler = result => chained.Resolve(mapper(result)); ErrorHandler errorHandler = delegate(Exception e) { @@ -427,12 +448,21 @@ namespace Implab { var promise = chained(result); - // notify chained operation that it's not needed - medium.Cancelled(() => promise.Cancel()); promise.Then( x => medium.Resolve(x), e => medium.Reject(e) ); + + // notify chained operation that it's not needed anymore + // порядок вызова Then, Cancelled важен, поскольку от этого + // зависит IsExclusive + medium.Cancelled(() => { + if(promise.IsExclusive) + promise.Cancel(); + }); + + // внешняя отмена связанной операции рассматривается как ошибка + promise.Cancelled(() => medium.Reject(new OperationCanceledException())); }; ErrorHandler errorHandler = delegate(Exception e) { @@ -531,7 +561,8 @@ namespace Implab { } void AddHandler(ResultHandler success, ErrorHandler error, Action cancel) { - Interlocked.Increment(ref m_childrenCount); + if (success != null || error != null) + Interlocked.Increment(ref m_childrenCount); HandlerDescriptor handler = new HandlerDescriptor { resultHandler = success, @@ -588,20 +619,6 @@ namespace Implab { } } - protected bool Cancel(bool dependencies) { - if (BeginTransit()) { - CompleteTransit(CancelledState); - OnStateChanged(); - - if (dependencies && m_parent != null && m_parent.IsExclusive) - m_parent.Cancel(); - - return true; - } else { - return false; - } - } - /// /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний. /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено. @@ -629,20 +646,25 @@ namespace Implab { for (int i = 0; i < promises.Count; i++) { var dest = i; - promises[i].Then( - x => { - result[dest] = x; - if(Interlocked.Decrement(ref pending) == 0) - promise.Resolve(result); - }, - e => promise.Reject(e) - ); + if (promises[i] != null) { + promises[i].Then( + x => { + result[dest] = x; + if (Interlocked.Decrement(ref pending) == 0) + promise.Resolve(result); + }, + e => promise.Reject(e) + ); + } else { + if (Interlocked.Decrement(ref pending) == 0) + promise.Resolve(result); + } } promise.Cancelled( () => { - foreach(var d in promises) - if(d.IsExclusive) + foreach (var d in promises) + if (d != null && d.IsExclusive) d.Cancel(); } ); @@ -650,6 +672,47 @@ namespace Implab { return promise; } + /// + /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при + /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний + /// игнорируются. + /// + /// Коллекция первичных обещаний, которые будут объеденены в одно. + /// Новое обещание, объединяющее в себе переданные. + /// + /// Если в коллекции встречаюься null, то они воспринимаются как выполненные обещания. + /// + public static IPromiseBase CreateComposite(ICollection promises) { + if (promises == null) + throw new ArgumentNullException(); + if (promises.Count == 0) + return Promise.ResultToPromise(null); + + int countdown = promises.Count; + + var result = new Promise(); + + foreach (var d in promises) { + if (d == null) { + if (Interlocked.Decrement(ref countdown) == 0) + result.Resolve(null); + } else { + d.Then(() => { + if (Interlocked.Decrement(ref countdown) == 0) + result.Resolve(null); + }); + } + } + + result.Cancelled(() => { + foreach (var d in promises) + if (d != null && d.IsExclusive) + d.Cancel(); + }); + + return result; + } + public static Promise ResultToPromise(T result) { var p = new Promise(); p.Resolve(result); @@ -665,5 +728,35 @@ namespace Implab { return p; } + #region IPromiseBase explicit implementation + + IPromiseBase IPromiseBase.Error(ErrorHandler error) { + return Error(error); + } + + IPromiseBase IPromiseBase.Anyway(Action handler) { + return Anyway(handler); + } + + IPromiseBase IPromiseBase.Finally(Action handler) { + return Finally(handler); + } + + IPromiseBase IPromiseBase.Cancelled(Action handler) { + return Cancelled(handler); + } + + void IPromiseBase.Join() { + Join(); + } + + void IPromiseBase.Join(int timeout) { + Join(timeout); + } + + #endregion + + + } }