changeset 72:d67b95eddaf4 v2

promises refactoring
author cin
date Thu, 04 Sep 2014 18:47:12 +0400
parents 1714fd8678ef
children 3b8393be3441
files Implab.Fx.Test/Implab.Fx.Test.csproj Implab.Fx/ControlBoundPromise.cs Implab.Fx/Implab.Fx.csproj Implab.Fx/PromiseHelpers.cs Implab.Test/Implab.Test.csproj Implab.sln Implab/IPromiseT.cs Implab/Implab.csproj Implab/JSON/JSONGrammar.cs Implab/JSON/JSONWriter.cs Implab/Parallels/ArrayTraits.cs Implab/Promise.cs Implab/PromiseExtensions.cs Implab/SyncContextPromise.cs Implab/TransientPromiseException.cs
diffstat 15 files changed, 612 insertions(+), 240 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Fx.Test/Implab.Fx.Test.csproj	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab.Fx.Test/Implab.Fx.Test.csproj	Thu Sep 04 18:47:12 2014 +0400
@@ -3,8 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>
-    </ProductVersion>
+    <ProductVersion>8.0.30703</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{2F31E405-E267-4195-A05D-574093C21209}</ProjectGuid>
     <OutputType>Library</OutputType>
@@ -45,11 +44,6 @@
     <Reference Include="WindowsBase" />
   </ItemGroup>
   <ItemGroup>
-    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
-      <Visible>False</Visible>
-    </CodeAnalysisDependentAssemblyPaths>
-  </ItemGroup>
-  <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="OverlayTest.cs" />
     <Compile Include="Sample\MainForm.cs">
@@ -68,9 +62,13 @@
   <ItemGroup>
     <EmbeddedResource Include="Sample\MainForm.resx">
       <DependentUpon>MainForm.cs</DependentUpon>
+      <LogicalName>
+      </LogicalName>
     </EmbeddedResource>
     <EmbeddedResource Include="Sample\OverlayForm.resx">
       <DependentUpon>OverlayForm.cs</DependentUpon>
+      <LogicalName>
+      </LogicalName>
     </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Fx/ControlBoundPromise.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -0,0 +1,30 @@
+using System.Windows.Forms;
+using System;
+
+
+namespace Implab.Fx {
+    public class ControlBoundPromise<T> : Promise<T> {
+        readonly Control m_target;
+
+        public ControlBoundPromise(Control target) {
+            Safe.ArgumentNotNull(target, "target");
+
+            m_target = target;
+        }
+
+        public ControlBoundPromise(Control target, IPromise parent, bool cancellable)
+            : base(parent, cancellable) {
+            Safe.ArgumentNotNull(target, "target");
+
+            m_target = target;
+        }
+
+        protected override void InvokeHandler(HandlerDescriptor handler) {
+            if (m_target.InvokeRequired)
+                m_target.BeginInvoke(new Action<HandlerDescriptor>(base.InvokeHandler), handler);
+            else
+                base.InvokeHandler(handler);
+        }
+    }
+}
+
--- a/Implab.Fx/Implab.Fx.csproj	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab.Fx/Implab.Fx.csproj	Thu Sep 04 18:47:12 2014 +0400
@@ -46,10 +46,11 @@
     <Compile Include="AnimationHelpers.cs" />
     <Compile Include="PromiseHelpers.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ControlBoundPromise.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Implab\Implab.csproj">
-      <Project>{99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}</Project>
+      <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
       <Name>Implab</Name>
     </ProjectReference>
   </ItemGroup>
--- a/Implab.Fx/PromiseHelpers.cs	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab.Fx/PromiseHelpers.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -1,7 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 using System.Windows.Forms;
 using System.Threading;
 
@@ -32,22 +29,14 @@
             if (ctl == null)
                 throw new ArgumentNullException("ctl");
 
-            var directed = new Promise<T>();
+            var directed = new ControlBoundPromise<T>(ctl,that,true);
 
             that.Then(
-                res =>
-                {
-                    if (ctl.InvokeRequired)
-                        ctl.Invoke(new Action<T>(directed.Resolve), res);
-                    else
-                        directed.Resolve(res);
-                },
+                directed.Resolve,
                 err =>
                 {
-                    if (ctl.InvokeRequired)
-                        ctl.Invoke(new Action<Exception>(directed.Reject), err);
-                    else
-                        directed.Reject(err);
+                    directed.Reject(err);
+                    return default(T);
                 }
             );
 
@@ -86,7 +75,10 @@
 
             that.Then(
                 res => sync.Post(state => d.Resolve(res), null),
-                err => sync.Post(state => d.Reject(err), null)
+                err => {
+                    sync.Post(state => d.Reject(err), null);
+                    return default(T);
+                }
             );
 
             return d;
--- a/Implab.Test/Implab.Test.csproj	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab.Test/Implab.Test.csproj	Thu Sep 04 18:47:12 2014 +0400
@@ -3,8 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>
-    </ProductVersion>
+    <ProductVersion>8.0.30703</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{63F92C0C-61BF-48C0-A377-8D67C3C661D0}</ProjectGuid>
     <OutputType>Library</OutputType>
@@ -40,11 +39,6 @@
     </Reference>
   </ItemGroup>
   <ItemGroup>
-    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
-      <Visible>False</Visible>
-    </CodeAnalysisDependentAssemblyPaths>
-  </ItemGroup>
-  <ItemGroup>
     <Compile Include="AsyncTests.cs" />
     <Compile Include="PromiseHelper.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
--- a/Implab.sln	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab.sln	Thu Sep 04 18:47:12 2014 +0400
@@ -17,22 +17,11 @@
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}"
 EndProject
 Global
-	GlobalSection(TestCaseManagementSettings) = postSolution
-		CategoryFile = Implab.vsmdi
-	EndGlobalSection
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Release|Any CPU = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
-		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
 		{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{06E706F8-6881-43EB-927E-FFC503AF6ABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -41,11 +30,223 @@
 		{2F31E405-E267-4195-A05D-574093C21209}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU
+		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+	EndGlobalSection
+	GlobalSection(MonoDevelopProperties) = preSolution
+		StartupItem = Implab\Implab.csproj
+		Policies = $0
+		$0.CSharpFormattingPolicy = $1
+		$1.IndentSwitchBody = True
+		$1.NamespaceBraceStyle = EndOfLine
+		$1.ClassBraceStyle = EndOfLine
+		$1.InterfaceBraceStyle = EndOfLine
+		$1.StructBraceStyle = EndOfLine
+		$1.EnumBraceStyle = EndOfLine
+		$1.MethodBraceStyle = EndOfLine
+		$1.ConstructorBraceStyle = EndOfLine
+		$1.DestructorBraceStyle = EndOfLine
+		$1.BeforeMethodDeclarationParentheses = False
+		$1.BeforeMethodCallParentheses = False
+		$1.BeforeConstructorDeclarationParentheses = False
+		$1.NewLineBeforeConstructorInitializerColon = NewLine
+		$1.NewLineAfterConstructorInitializerColon = SameLine
+		$1.BeforeIndexerDeclarationBracket = False
+		$1.BeforeDelegateDeclarationParentheses = False
+		$1.NewParentheses = False
+		$1.SpacesBeforeBrackets = False
+		$1.inheritsSet = Mono
+		$1.inheritsScope = text/x-csharp
+		$1.scope = text/x-csharp
+		$0.TextStylePolicy = $2
+		$2.FileWidth = 120
+		$2.EolMarker = Unix
+		$2.inheritsSet = VisualStudio
+		$2.inheritsScope = text/plain
+		$2.scope = text/x-csharp
+		$0.DotNetNamingPolicy = $3
+		$3.DirectoryNamespaceAssociation = PrefixedHierarchical
+		$3.ResourceNamePolicy = MSBuild
+		$0.TextStylePolicy = $4
+		$4.FileWidth = 120
+		$4.TabsToSpaces = False
+		$4.inheritsSet = VisualStudio
+		$4.inheritsScope = text/plain
+		$4.scope = application/xml
+		$0.XmlFormattingPolicy = $5
+		$5.inheritsSet = Mono
+		$5.inheritsScope = application/xml
+		$5.scope = application/xml
+		$0.TextStylePolicy = $6
+		$6.FileWidth = 120
+		$6.TabsToSpaces = False
+		$6.inheritsSet = VisualStudio
+		$6.inheritsScope = text/plain
+		$6.scope = text/plain
+		$0.NameConventionPolicy = $7
+		$7.Rules = $8
+		$8.NamingRule = $9
+		$9.Name = Namespaces
+		$9.AffectedEntity = Namespace
+		$9.VisibilityMask = VisibilityMask
+		$9.NamingStyle = PascalCase
+		$9.IncludeInstanceMembers = True
+		$9.IncludeStaticEntities = True
+		$8.NamingRule = $10
+		$10.Name = Types
+		$10.AffectedEntity = Class, Struct, Enum, Delegate
+		$10.VisibilityMask = VisibilityMask
+		$10.NamingStyle = PascalCase
+		$10.IncludeInstanceMembers = True
+		$10.IncludeStaticEntities = True
+		$8.NamingRule = $11
+		$11.Name = Interfaces
+		$11.RequiredPrefixes = $12
+		$12.String = I
+		$11.AffectedEntity = Interface
+		$11.VisibilityMask = VisibilityMask
+		$11.NamingStyle = PascalCase
+		$11.IncludeInstanceMembers = True
+		$11.IncludeStaticEntities = True
+		$8.NamingRule = $13
+		$13.Name = Attributes
+		$13.RequiredSuffixes = $14
+		$14.String = Attribute
+		$13.AffectedEntity = CustomAttributes
+		$13.VisibilityMask = VisibilityMask
+		$13.NamingStyle = PascalCase
+		$13.IncludeInstanceMembers = True
+		$13.IncludeStaticEntities = True
+		$8.NamingRule = $15
+		$15.Name = Event Arguments
+		$15.RequiredSuffixes = $16
+		$16.String = EventArgs
+		$15.AffectedEntity = CustomEventArgs
+		$15.VisibilityMask = VisibilityMask
+		$15.NamingStyle = PascalCase
+		$15.IncludeInstanceMembers = True
+		$15.IncludeStaticEntities = True
+		$8.NamingRule = $17
+		$17.Name = Exceptions
+		$17.RequiredSuffixes = $18
+		$18.String = Exception
+		$17.AffectedEntity = CustomExceptions
+		$17.VisibilityMask = VisibilityMask
+		$17.NamingStyle = PascalCase
+		$17.IncludeInstanceMembers = True
+		$17.IncludeStaticEntities = True
+		$8.NamingRule = $19
+		$19.Name = Methods
+		$19.AffectedEntity = Methods
+		$19.VisibilityMask = VisibilityMask
+		$19.NamingStyle = PascalCase
+		$19.IncludeInstanceMembers = True
+		$19.IncludeStaticEntities = True
+		$8.NamingRule = $20
+		$20.Name = Static Readonly Fields
+		$20.AffectedEntity = ReadonlyField
+		$20.VisibilityMask = Internal, Protected, Public
+		$20.NamingStyle = CamelCase
+		$20.IncludeInstanceMembers = False
+		$20.IncludeStaticEntities = True
+		$8.NamingRule = $21
+		$21.Name = Fields (Non Private)
+		$21.AffectedEntity = Field
+		$21.VisibilityMask = Internal, Public
+		$21.NamingStyle = CamelCase
+		$21.IncludeInstanceMembers = True
+		$21.IncludeStaticEntities = True
+		$8.NamingRule = $22
+		$22.Name = ReadOnly Fields (Non Private)
+		$22.AffectedEntity = ReadonlyField
+		$22.VisibilityMask = Internal, Public
+		$22.NamingStyle = CamelCase
+		$22.IncludeInstanceMembers = True
+		$22.IncludeStaticEntities = False
+		$8.NamingRule = $23
+		$23.Name = Fields (Private)
+		$23.RequiredPrefixes = $24
+		$24.String = m_
+		$23.AffectedEntity = Field, ReadonlyField
+		$23.VisibilityMask = Private, Protected
+		$23.NamingStyle = CamelCase
+		$23.IncludeInstanceMembers = True
+		$23.IncludeStaticEntities = False
+		$8.NamingRule = $25
+		$25.Name = Static Fields (Private)
+		$25.RequiredPrefixes = $26
+		$26.String = _
+		$25.AffectedEntity = Field
+		$25.VisibilityMask = Private
+		$25.NamingStyle = CamelCase
+		$25.IncludeInstanceMembers = False
+		$25.IncludeStaticEntities = True
+		$8.NamingRule = $27
+		$27.Name = ReadOnly Fields (Private)
+		$27.RequiredPrefixes = $28
+		$28.String = m_
+		$27.AffectedEntity = ReadonlyField
+		$27.VisibilityMask = Private, Protected
+		$27.NamingStyle = CamelCase
+		$27.IncludeInstanceMembers = True
+		$27.IncludeStaticEntities = False
+		$8.NamingRule = $29
+		$29.Name = Constant Fields
+		$29.AffectedEntity = ConstantField
+		$29.VisibilityMask = VisibilityMask
+		$29.NamingStyle = AllUpper
+		$29.IncludeInstanceMembers = True
+		$29.IncludeStaticEntities = True
+		$8.NamingRule = $30
+		$30.Name = Properties
+		$30.AffectedEntity = Property
+		$30.VisibilityMask = VisibilityMask
+		$30.NamingStyle = PascalCase
+		$30.IncludeInstanceMembers = True
+		$30.IncludeStaticEntities = True
+		$8.NamingRule = $31
+		$31.Name = Events
+		$31.AffectedEntity = Event
+		$31.VisibilityMask = VisibilityMask
+		$31.NamingStyle = PascalCase
+		$31.IncludeInstanceMembers = True
+		$31.IncludeStaticEntities = True
+		$8.NamingRule = $32
+		$32.Name = Enum Members
+		$32.AffectedEntity = EnumMember
+		$32.VisibilityMask = VisibilityMask
+		$32.NamingStyle = PascalCase
+		$32.IncludeInstanceMembers = True
+		$32.IncludeStaticEntities = True
+		$8.NamingRule = $33
+		$33.Name = Parameters
+		$33.AffectedEntity = Parameter, LocalVariable
+		$33.VisibilityMask = VisibilityMask
+		$33.NamingStyle = CamelCase
+		$33.IncludeInstanceMembers = True
+		$33.IncludeStaticEntities = True
+		$8.NamingRule = $34
+		$34.Name = Type Parameters
+		$34.RequiredPrefixes = $35
+		$35.String = T
+		$34.AffectedEntity = TypeParameter
+		$34.VisibilityMask = VisibilityMask
+		$34.NamingStyle = PascalCase
+		$34.IncludeInstanceMembers = True
+		$34.IncludeStaticEntities = True
+	EndGlobalSection
+	GlobalSection(TestCaseManagementSettings) = postSolution
+		CategoryFile = Implab.vsmdi
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
-	GlobalSection(MonoDevelopProperties) = preSolution
-		StartupItem = Implab\Implab.csproj
-	EndGlobalSection
 EndGlobal
--- a/Implab/IPromiseT.cs	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab/IPromiseT.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -11,16 +11,14 @@
         new T Join();
         new T Join(int timeout);
 
-        IPromise<T> Then(ResultHandler<T> success, ErrorHandler error);
         IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
         IPromise<T> Then(ResultHandler<T> success);
-        new IPromise<T> Error(ErrorHandler error);
         IPromise<T> Error(ErrorHandler<T> error);
 
-        IPromise<T2> Map<T2>(ResultMapper<T,T2> mapper, ErrorHandler error);
+        IPromise<T2> Map<T2>(ResultMapper<T,T2> mapper, ErrorHandler<T> error);
         IPromise<T2> Map<T2>(ResultMapper<T, T2> mapper);
 
-        IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained, ErrorHandler error);
+        IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained, ErrorHandler<T> error);
         IPromise<T2> Chain<T2>(ChainedOperation<T, T2> chained);
 
         new IPromise<T> Cancelled(Action handler);
--- a/Implab/Implab.csproj	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab/Implab.csproj	Thu Sep 04 18:47:12 2014 +0400
@@ -7,6 +7,8 @@
     <OutputType>Library</OutputType>
     <RootNamespace>Implab</RootNamespace>
     <AssemblyName>Implab</AssemblyName>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -99,7 +101,79 @@
     <Compile Include="Parallels\AsyncPool.cs" />
     <Compile Include="Safe.cs" />
     <Compile Include="ValueEventArgs.cs" />
+    <Compile Include="PromiseExtensions.cs" />
+    <Compile Include="TransientPromiseException.cs" />
+    <Compile Include="SyncContextPromise.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup />
+  <ProjectExtensions>
+    <MonoDevelop>
+      <Properties>
+        <Policies>
+          <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
+          <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
+          <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
+          <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
+          <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
+          <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
+          <NameConventionPolicy>
+            <Rules>
+              <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
+                <RequiredPrefixes>
+                  <String>I</String>
+                </RequiredPrefixes>
+              </NamingRule>
+              <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
+                <RequiredSuffixes>
+                  <String>Attribute</String>
+                </RequiredSuffixes>
+              </NamingRule>
+              <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
+                <RequiredSuffixes>
+                  <String>EventArgs</String>
+                </RequiredSuffixes>
+              </NamingRule>
+              <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
+                <RequiredSuffixes>
+                  <String>Exception</String>
+                </RequiredSuffixes>
+              </NamingRule>
+              <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
+              <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
+              <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
+                <RequiredPrefixes>
+                  <String>m_</String>
+                </RequiredPrefixes>
+              </NamingRule>
+              <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
+                <RequiredPrefixes>
+                  <String>_</String>
+                </RequiredPrefixes>
+              </NamingRule>
+              <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
+                <RequiredPrefixes>
+                  <String>m_</String>
+                </RequiredPrefixes>
+              </NamingRule>
+              <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
+              <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
+                <RequiredPrefixes>
+                  <String>T</String>
+                </RequiredPrefixes>
+              </NamingRule>
+            </Rules>
+          </NameConventionPolicy>
+        </Policies>
+      </Properties>
+    </MonoDevelop>
+  </ProjectExtensions>
 </Project>
\ No newline at end of file
--- a/Implab/JSON/JSONGrammar.cs	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab/JSON/JSONGrammar.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -7,7 +7,7 @@
 
 namespace Implab.JSON {
     internal class JSONGrammar : Grammar<JSONGrammar> {
-        public enum TokenType : int{
+        public enum TokenType : int {
             None,
             BeginObject,
             EndObject,
@@ -53,7 +53,6 @@
             var backSlash = SymbolToken('\\');
             var specialEscapeChars = SymbolSetToken('\\', '"', '/', 'b', 'f', 't', 'n', 'r');
             var unicodeEspace = SymbolToken('u').Cat(hexDigit.Repeat(4));
-            var escape = backSlash.Cat(specialEscapeChars.Or(unicodeEspace));
             var whitespace = SymbolSetToken('\n', '\r', '\t', ' ').EClosure();
             var beginObject = whitespace.Cat(SymbolToken('{')).Cat(whitespace);
             var endObject = whitespace.Cat(SymbolToken('}')).Cat(whitespace);
@@ -65,9 +64,6 @@
             var number = minus.Optional().Cat(integer).Cat(frac.Optional()).Cat(exp.Optional());
             var literal = letters.Closure();
             var unescaped = SymbolTokenExcept(Enumerable.Range(0, 0x20).Union(new int[] { '\\', '"' }).Select(x => (char)x));
-            var character = unescaped.Or(escape);
-            var str = quote.Cat(character.EClosure()).Cat(quote);
-            
 
             var jsonExpression =
                 number.Tag(TokenType.Number)
@@ -86,13 +82,7 @@
                 .Or(backSlash.Cat(specialEscapeChars).Tag(TokenType.EscapedChar))
                 .Or(backSlash.Cat(unicodeEspace).Tag(TokenType.EscapedUnicode))
                 .Or(unescaped.Closure().Tag(TokenType.UnescapedChar));
-
-            var jsonNumberExpression =
-                minus.Tag(TokenType.Minus)
-                .Or(SymbolToken('+').Tag(TokenType.Plus))
-                .Or(digit.Closure().Tag(TokenType.Integer))
-                .Or(dot.Tag(TokenType.Dot))
-                .Or(expSign.Tag(TokenType.Exp));
+                    
 
             m_jsonDFA = BuildDFA(jsonExpression);
             m_stringDFA = BuildDFA(jsonStringExpression);
--- a/Implab/JSON/JSONWriter.cs	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab/JSON/JSONWriter.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -15,7 +15,8 @@
         Context m_context;
 
         TextWriter m_writer;
-        bool m_indent;
+        readonly bool m_indent = true;
+        readonly int m_indentSize = 4;
 
         static readonly char [] _escapeBKS,
             _escapeFWD,
@@ -43,13 +44,33 @@
             m_writer = writer;
         }
 
+        public JSONWriter(TextWriter writer, bool indent) {
+            Safe.ArgumentNotNull(writer, "writer");
+
+            m_writer = writer;
+            m_indent = indent;
+        }
+
+        void WriteIndent() {
+            if (m_indent) {
+                var indent = new char[m_contextStack.Count * m_indentSize + 1];
+                indent[0] = '\n';
+                for (int i = 1; i < indent.Length; i++)
+                    indent[i] = ' ';
+                m_writer.Write(new String(indent));
+            } else {
+                m_writer.Write(' ');
+            }
+        }
+
         void WriteMemberName(string name) {
             Safe.ArgumentNotEmpty(name, "name");
             if (m_context.element != JSONElementContext.Object)
                 OperationNotApplicable("WriteMember");
             if (m_context.needComma)
-                m_writer.Write(", ");
-            // TODO indent
+                m_writer.Write(",");
+
+            WriteIndent();
             m_context.needComma = true;
             Write(name);
             m_writer.Write(" : ");
@@ -70,13 +91,12 @@
             Write(value);
         }
 
-
-
         public void WriteValue(string value) {
             if (m_context.element != JSONElementContext.Array)
                 OperationNotApplicable("WriteValue");
             if (m_context.needComma)
-                m_writer.Write(", ");
+                m_writer.Write(",");
+            WriteIndent();
             m_context.needComma = true;
 
             Write(value);
@@ -86,9 +106,10 @@
             if (m_context.element != JSONElementContext.Array)
                 OperationNotApplicable("WriteValue");
             if (m_context.needComma)
-                m_writer.Write(", ");
+                m_writer.Write(",");
             m_context.needComma = true;
 
+            WriteIndent();
             Write(value);
         }
 
@@ -96,9 +117,10 @@
             if (m_context.element != JSONElementContext.Array)
                 OperationNotApplicable("WriteValue");
             if (m_context.needComma)
-                m_writer.Write(", ");
+                m_writer.Write(",");
             m_context.needComma = true;
 
+            WriteIndent();
             Write(value);
         }
         
@@ -106,13 +128,16 @@
             if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
                 OperationNotApplicable("BeginObject");
             if (m_context.needComma)
-                m_writer.Write(", ");
+                m_writer.Write(",");
+
+            WriteIndent();
+
             m_context.needComma = true;
 
             m_contextStack.Push(m_context);
 
             m_context = new Context { element = JSONElementContext.Object, needComma = false };
-            m_writer.Write("{ ");
+            m_writer.Write("{");
         }
 
         public void BeginObject(string name) {
@@ -121,28 +146,31 @@
             m_contextStack.Push(m_context);
 
             m_context = new Context { element = JSONElementContext.Object, needComma = false };
-            m_writer.Write("{ ");
+            m_writer.Write("{");
         }
 
         public void EndObject() {
             if (m_context.element != JSONElementContext.Object)
                 OperationNotApplicable("EndArray");
-            
-            m_writer.Write(" }");
+
             m_context = m_contextStack.Pop();
+            WriteIndent();
+            m_writer.Write("}");
         }
 
         public void BeginArray() {
             if (m_context.element != JSONElementContext.None && m_context.element != JSONElementContext.Array)
                 throw new InvalidOperationException();
-            if (m_context.needComma)
-                m_writer.Write(", ");
+            if (m_context.needComma) {
+                m_writer.Write(",");
+
+            }
             m_context.needComma = true;
 
+            WriteIndent();
             m_contextStack.Push(m_context);
-
             m_context = new Context { element = JSONElementContext.Array, needComma = false };
-            m_writer.Write("[ ");
+            m_writer.Write("[");
         }
 
         public void BeginArray(string name) {
@@ -151,15 +179,16 @@
             m_contextStack.Push(m_context);
 
             m_context = new Context { element = JSONElementContext.Array, needComma = false };
-            m_writer.Write("[ ");
+            m_writer.Write("[");
         }
 
         public void EndArray() {
             if (m_context.element != JSONElementContext.Array)
                 OperationNotApplicable("EndArray");
 
-            m_writer.Write(" ]");
             m_context = m_contextStack.Pop();
+            WriteIndent();
+            m_writer.Write("]");
         }
 
         void Write(bool value) {
--- a/Implab/Parallels/ArrayTraits.cs	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab/Parallels/ArrayTraits.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -174,7 +174,10 @@
                                 if (left == 0)
                                     promise.Resolve(res);
                             },
-                            e => promise.Reject(e)
+                            e => {
+                                promise.Reject(e);
+                                throw new TransientPromiseException(e);
+                            }
                         );
 
                     } catch (Exception e) {
--- a/Implab/Promise.cs	Wed Sep 03 18:34:02 2014 +0400
+++ b/Implab/Promise.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -10,8 +10,8 @@
     public delegate void ErrorHandler(Exception e);
     public delegate T ErrorHandler<out T>(Exception e);
     public delegate void ResultHandler<in T>(T result);
-    public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result);
-    public delegate IPromise<TNew> ChainedOperation<in TSrc, TNew>(TSrc result);
+    public delegate TNew ResultMapper<in TSrc,out TNew>(TSrc result);
+    public delegate IPromise<TNew> ChainedOperation<in TSrc,TNew>(TSrc result);
 
     /// <summary>
     /// Класс для асинхронного получения результатов. Так называемое "обещание".
@@ -51,32 +51,51 @@
 
         protected struct HandlerDescriptor {
             public ResultHandler<T> resultHandler;
-            public ErrorHandler errorHandler;
+            public ErrorHandler<T> errorHandler;
             public Action cancellHandler;
+            public Promise<T> medium;
 
             public void Resolve(T result) {
-                if (resultHandler != null)
+                if (resultHandler != null) {
                     try {
                         resultHandler(result);
                     } catch (Exception e) {
                         Reject(e);
+                        return;
                     }
+                }
+                if (medium != null)
+                    medium.Resolve(result);
             }
 
             public void Reject(Exception err) {
-                if (errorHandler != null)
+                if (errorHandler != null) {
                     try {
-                        errorHandler(err);
-                    } catch {
+                        var res = errorHandler(err);
+                        if (medium != null)
+                            medium.Resolve(res);
+                    } catch (TransientPromiseException err2) {
+                        if (medium != null)
+                            medium.Reject(err2.InnerException);
+                    } catch (Exception err2) {
+                        if (medium != null)
+                            medium.Reject(err2);
                     }
+                } else if (medium != null)
+                    medium.Reject(err);
             }
 
             public void Cancel() {
-                if (cancellHandler != null)
+                if (cancellHandler != null) {
                     try {
                         cancellHandler();
-                    } catch {
+                    } catch (Exception err) {
+                        Reject(err);
+                        return;
                     }
+                }
+                if (medium != null)
+                    medium.Cancel();
             }
         }
 
@@ -102,14 +121,10 @@
         public Promise(IPromise parent, bool cancellable) {
             m_cancellable = cancellable;
             if (parent != null)
-                AddHandler(
-                    null,
-                    null,
-                    () => {
-                        if (parent.IsExclusive)
-                            parent.Cancel();
-                    }
-                );
+                Cancelled(() => {
+                    if (parent.IsExclusive)
+                        parent.Cancel();
+                });
         }
 
         bool BeginTransit() {
@@ -197,13 +212,12 @@
         /// </summary>
         /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
         public bool Cancel() {
-            if (BeginTransit()) {
+            if (m_cancellable && BeginTransit()) {
                 CompleteTransit(CANCELLED_STATE);
                 OnStateChanged();
                 return true;
-            } else {
-                return false;
             }
+            return false;
         }
 
         // сделано для возвращаемого типа void
@@ -216,55 +230,6 @@
         /// </summary>
         /// <param name="success">The handler of the successfully completed operation.
         /// This handler will recieve an operation result as a parameter.</param>
-        /// <param name="error">Handles an exception that may occur during the operation.</param>
-        /// <returns>The new promise chained to this one.</returns>
-        public IPromise<T> Then(ResultHandler<T> success, ErrorHandler error) {
-            if (success == null && error == null)
-                return this;
-
-            var medium = new Promise<T>(this, true);
-
-            ResultHandler<T> resultHandler;
-            if (success != null)
-                resultHandler = x => {
-                    success(x);
-                    medium.Resolve(x);
-                };
-            else
-                resultHandler = medium.Resolve;
-
-            ErrorHandler errorHandler;
-            if (error != null)
-                errorHandler = x => {
-                    // несмотря на то, что обработчик ошибки вызывается безопасно,
-                    // т.е. возникшие в нем ошибки будут подавлены, нам нужно
-                    // гарантировать, что ошибка будет передана дальше по цепочке обещаний
-                    try {
-                        error(x);
-                    } catch { }
-                    medium.Reject(x);
-                };
-            else
-                errorHandler = medium.Reject;
-
-            AddHandler(resultHandler, errorHandler, medium.InternalCancel);
-
-            return medium;
-        }
-
-        public IPromise Then(Action success, ErrorHandler error) {
-            return Then(x => success(), error);
-        }
-
-        public IPromise Then(Action success) {
-            return Then(x => success());
-        }
-
-        /// <summary>
-        /// Adds new handlers to this promise.
-        /// </summary>
-        /// <param name="success">The handler of the successfully completed operation.
-        /// This handler will recieve an operation result as a parameter.</param>
         /// <param name="error">Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.</param>
         /// <returns>The new promise chained to this one.</returns>
         public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
@@ -273,33 +238,25 @@
 
             var medium = new Promise<T>(this, true);
 
-            ResultHandler<T> resultHandler;
-            ErrorHandler errorHandler;
-
-            if (success != null)
-                resultHandler = x => {
-                    success(x);
-                    medium.Resolve(x);
-                };
-            else
-                resultHandler = medium.Resolve;
-
-            if (error != null)
-                errorHandler = x => {
-                    try {
-                        medium.Resolve(error(x));
-                    } catch (Exception e) {
-                        medium.Reject(e);
-                    }
-                };
-            else
-                errorHandler = medium.Reject;
-
-            AddHandler(resultHandler, errorHandler, medium.InternalCancel);
+            AddHandler(success, error, null, medium);
 
             return medium;
         }
 
+        public IPromise Then(Action success, ErrorHandler error) {
+            return Then(
+                x => success(),
+                e => {
+                    error(e);
+                    return default(T);
+                }
+            );
+        }
+
+        public IPromise Then(Action success) {
+            return Then(x => success());
+        }
+
 
         public IPromise<T> Then(ResultHandler<T> success) {
             if (success == null)
@@ -307,23 +264,28 @@
 
             var medium = new Promise<T>(this, true);
 
-            ResultHandler<T> resultHandler;
-
-            if (success != null)
-                resultHandler = x => {
-                    success(x);
-                    medium.Resolve(x);
-                };
-            else
-                resultHandler = medium.Resolve;
-
-            AddHandler(resultHandler, medium.Reject, medium.InternalCancel);
+            AddHandler(success, null, null, medium);
 
             return medium;
         }
 
-        public IPromise<T> Error(ErrorHandler error) {
-            return Then((ResultHandler<T>)null, error);
+        public IPromise Error(ErrorHandler error) {
+            if (error == null)
+                return this;
+
+            var medium = new Promise<T>(this, true);
+
+            AddHandler(
+                null,
+                e => {
+                    error(e);
+                    return default(T);
+                },
+                null,
+                medium
+            );
+
+            return medium;
         }
 
         /// <summary>
@@ -340,17 +302,7 @@
 
             var medium = new Promise<T>(this, true);
 
-            AddHandler(
-                x => medium.Resolve(x),
-                e => {
-                    try {
-                        medium.Resolve(handler(e));
-                    } catch (Exception e2) {
-                        medium.Reject(e2);
-                    }
-                },
-                medium.InternalCancel
-            );
+            AddHandler(null, handler, null, medium);
 
             return medium;
         }
@@ -359,27 +311,16 @@
             if (handler == null)
                 return this;
 
-            var medium = new Promise<T>(this,true);
+            var medium = new Promise<T>(this, true);
 
             AddHandler(
-                x => {
-                    // to avoid handler being called multiple times we handle exception by ourselfs
-                    try {
-                        handler();
-                        medium.Resolve(x);
-                    } catch (Exception e) {
-                        medium.Reject(e);
-                    }
+                x => handler(),
+                e => {
+                    handler();
+                    throw new TransientPromiseException(e);
                 },
-
-                e => {
-                    try {
-                        handler();
-                    } catch { }
-                    medium.Reject(e);
-                },
-
-                medium.InternalCancel
+                null,
+                medium
             );
 
             return medium;
@@ -393,28 +334,37 @@
         /// <param name="error">Обработчик ошибки. Данный обработчик получит
         /// исключение возникшее при выполнении операции.</param>
         /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
-        public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
+        public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) {
             if (mapper == null)
                 throw new ArgumentNullException("mapper");
 
             // создаем прицепленное обещание
-            var chained = new Promise<TNew>(this,true);
+            var chained = new Promise<TNew>(this, true);
 
             ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result));
-            ErrorHandler errorHandler = delegate(Exception e) {
-                if (error != null)
+            ErrorHandler<T> errorHandler;
+            if (error != null)
+                errorHandler = e => {
                     try {
-                        error(e);
-                    } catch { }
-                // в случае ошибки нужно передать исключение дальше по цепочке
-                chained.Reject(e);
-            };
+                        return error(e);
+                    } catch (Exception e2) {
+                        // в случае ошибки нужно передать исключение дальше по цепочке
+                        chained.Reject(e2);
+                    }
+                    return default(T);
+                };
+            else
+                errorHandler = e => {
+                    chained.Reject(e);
+                    return default(T);
+                };
 
 
             AddHandler(
                 resultHandler,
                 errorHandler,
-                chained.InternalCancel
+                chained.InternalCancel,
+                null
             );
 
             return chained;
@@ -434,7 +384,7 @@
         /// <param name="error">Обработчик ошибки. Данный обработчик получит
         /// исключение возникшее при выполнении текуещй операции.</param>
         /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
-        public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
+        public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler<T> error) {
 
             // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
             // создать посредника, к которому будут подвызяваться следующие обработчики.
@@ -449,15 +399,18 @@
                 var promise = chained(result);
 
                 promise.Then(
-                    x => medium.Resolve(x),
-                    e => medium.Reject(e)
+                    medium.Resolve,
+                    err => {
+                        medium.Reject(err);
+                        throw new TransientPromiseException(err);
+                    }
                 );
                 
                 // notify chained operation that it's not needed anymore
                 // порядок вызова Then, Cancelled важен, поскольку от этого
                 // зависит IsExclusive
                 medium.Cancelled(() => {
-                    if(promise.IsExclusive)
+                    if (promise.IsExclusive)
                         promise.Cancel();
                 });
 
@@ -465,17 +418,25 @@
                 promise.Cancelled(() => medium.Reject(new OperationCanceledException()));
             };
 
-            ErrorHandler errorHandler = delegate(Exception e) {
-                if (error != null)
-                    error(e);
+            ErrorHandler<T> errorHandler = delegate(Exception e) {
+                if (error != null) {
+                    try {
+                        return error(e);
+                    } catch (Exception e2) {
+                        medium.Reject(e2);
+                        return default(T);
+                    }
+                }
                 // в случае ошибки нужно передать исключение дальше по цепочке
                 medium.Reject(e);
+                return default(T);
             };
 
             AddHandler(
                 resultHandler,
                 errorHandler,
-                medium.InternalCancel
+                medium.InternalCancel,
+                null
             );
 
             return medium;
@@ -486,7 +447,7 @@
         }
 
         public IPromise<T> Cancelled(Action handler) {
-            AddHandler(null, null, handler);
+            AddHandler(null, null, handler, null);
             return this;
         }
 
@@ -500,8 +461,12 @@
                 throw new ArgumentNullException("handler");
             AddHandler(
                 x => handler(),
-                e => handler(),
-                handler
+                e => {
+                    handler();
+                    throw new TransientPromiseException(e);
+                },
+                handler,
+                null
             );
             return this;
         }
@@ -560,14 +525,15 @@
             return Join(Timeout.Infinite);
         }
 
-        void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) {
+        void AddHandler(ResultHandler<T> success, ErrorHandler<T> error, Action cancel, Promise<T> medium) {
             if (success != null || error != null)
                 Interlocked.Increment(ref m_childrenCount);
 
-            HandlerDescriptor handler = new HandlerDescriptor {
+            var handler = new HandlerDescriptor {
                 resultHandler = success,
                 errorHandler = error,
-                cancellHandler = cancel
+                cancellHandler = cancel,
+                medium = medium
             };
 
             bool queued;
@@ -653,7 +619,10 @@
                             if (Interlocked.Decrement(ref pending) == 0)
                                 promise.Resolve(result);
                         },
-                        e => promise.Reject(e)
+                        e => {
+                            promise.Reject(e);
+                            return default(T);
+                        }
                     );
                 } else {
                     if (Interlocked.Decrement(ref pending) == 0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/PromiseExtensions.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -0,0 +1,38 @@
+using System.Threading;
+
+namespace Implab {
+    public static class PromiseExtensions {
+        public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) {
+            var context = SynchronizationContext.Current;
+            if (context == null)
+                return that;
+
+            var p = new SyncContextPromise<T>(context, that, true);
+
+            that.Then(
+                x => p.Resolve(x),
+                e => {
+                    p.Reject(e);
+                    return default(T);
+                }
+            );
+            return p;
+        }
+
+        public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) {
+            Safe.ArgumentNotNull(context, "context");
+
+            var p = new SyncContextPromise<T>(context, that, true);
+
+            that.Then(
+                x => p.Resolve(x),
+                e => {
+                    p.Reject(e);
+                    return default(T);
+                }
+            );
+            return p;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/SyncContextPromise.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -0,0 +1,22 @@
+using System.Threading;
+
+namespace Implab {
+    public class SyncContextPromise<T> : Promise<T> {
+        readonly SynchronizationContext m_context;
+
+        public SyncContextPromise(SynchronizationContext context) {
+            Safe.ArgumentNotNull(context, "context");
+            m_context = context;
+        }
+
+        public SyncContextPromise(SynchronizationContext context, IPromise parent, bool cancellable)
+            : base(parent, cancellable) {
+            Safe.ArgumentNotNull(context, "context");
+            m_context = context;
+        }
+        protected override void InvokeHandler(HandlerDescriptor handler) {
+            m_context.Post(x => base.InvokeHandler(handler),null);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/TransientPromiseException.cs	Thu Sep 04 18:47:12 2014 +0400
@@ -0,0 +1,33 @@
+using System;
+
+namespace Implab {
+
+    [Serializable]
+    public class TransientPromiseException : Exception {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PromiseFailedException"/> class.
+        /// </summary>
+        /// <param name="inner">The exception that is the cause of the current exception.</param>
+        public TransientPromiseException(Exception inner) : base("The preceding promise has failed", inner) {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PromiseFailedException"/> class
+        /// </summary>
+        /// <param name="message">A <see cref="T:System.String"/> that describes the exception. </param>
+        /// <param name="inner">The exception that is the cause of the current exception. </param>
+        public TransientPromiseException(string message, Exception inner)
+            : base(message, inner) {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PromiseFailedException"/> class
+        /// </summary>
+        /// <param name="context">The contextual information about the source or destination.</param>
+        /// <param name="info">The object that holds the serialized object data.</param>
+        protected TransientPromiseException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
+            : base(info, context) {
+        }
+    }
+}
+