diff --git a/Implab.Fx.Test/Implab.Fx.Test.csproj b/Implab.Fx.Test/Implab.Fx.Test.csproj
--- a/Implab.Fx.Test/Implab.Fx.Test.csproj
+++ b/Implab.Fx.Test/Implab.Fx.Test.csproj
@@ -3,8 +3,7 @@
   
     Debug
     AnyCPU
-    
-    
+    8.0.30703
     2.0
     {2F31E405-E267-4195-A05D-574093C21209}
     Library
@@ -45,11 +44,6 @@
     
   
   
-    
-      False
-    
-  
-  
     
     
     
@@ -68,9 +62,13 @@
   
     
       MainForm.cs
+      
+      
     
     
       OverlayForm.cs
+      
+      
     
   
   
diff --git a/Implab.Fx/ControlBoundPromise.cs b/Implab.Fx/ControlBoundPromise.cs
new file mode 100644
--- /dev/null
+++ b/Implab.Fx/ControlBoundPromise.cs
@@ -0,0 +1,30 @@
+using System.Windows.Forms;
+using System;
+
+
+namespace Implab.Fx {
+    public class ControlBoundPromise : Promise {
+        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(base.InvokeHandler), handler);
+            else
+                base.InvokeHandler(handler);
+        }
+    }
+}
+
diff --git a/Implab.Fx/Implab.Fx.csproj b/Implab.Fx/Implab.Fx.csproj
--- a/Implab.Fx/Implab.Fx.csproj
+++ b/Implab.Fx/Implab.Fx.csproj
@@ -46,10 +46,11 @@
     
     
     
+    
   
   
     
-      {99B95D0D-9CF9-4F70-8ADF-F4D0AA5CB0D9}
+      {F550F1F8-8746-4AD0-9614-855F4C4B7F05}
       Implab
     
   
diff --git a/Implab.Fx/PromiseHelpers.cs b/Implab.Fx/PromiseHelpers.cs
--- a/Implab.Fx/PromiseHelpers.cs
+++ b/Implab.Fx/PromiseHelpers.cs
@@ -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 @@ namespace Implab.Fx
             if (ctl == null)
                 throw new ArgumentNullException("ctl");
 
-            var directed = new Promise();
+            var directed = new ControlBoundPromise(ctl,that,true);
 
             that.Then(
-                res =>
-                {
-                    if (ctl.InvokeRequired)
-                        ctl.Invoke(new Action(directed.Resolve), res);
-                    else
-                        directed.Resolve(res);
-                },
+                directed.Resolve,
                 err =>
                 {
-                    if (ctl.InvokeRequired)
-                        ctl.Invoke(new Action(directed.Reject), err);
-                    else
-                        directed.Reject(err);
+                    directed.Reject(err);
+                    return default(T);
                 }
             );
 
@@ -86,7 +75,10 @@ namespace Implab.Fx
 
             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;
diff --git a/Implab.Test/Implab.Test.csproj b/Implab.Test/Implab.Test.csproj
--- a/Implab.Test/Implab.Test.csproj
+++ b/Implab.Test/Implab.Test.csproj
@@ -3,8 +3,7 @@
   
     Debug
     AnyCPU
-    
-    
+    8.0.30703
     2.0
     {63F92C0C-61BF-48C0-A377-8D67C3C661D0}
     Library
@@ -40,11 +39,6 @@
     
   
   
-    
-      False
-    
-  
-  
     
     
     
diff --git a/Implab.sln b/Implab.sln
--- a/Implab.sln
+++ b/Implab.sln
@@ -17,22 +17,11 @@ EndProject
 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 @@ Global
 		{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
diff --git a/Implab/IPromiseT.cs b/Implab/IPromiseT.cs
--- a/Implab/IPromiseT.cs
+++ b/Implab/IPromiseT.cs
@@ -11,16 +11,14 @@ namespace Implab
         new T Join();
         new T Join(int timeout);
 
-        IPromise Then(ResultHandler success, ErrorHandler error);
         IPromise Then(ResultHandler success, ErrorHandler error);
         IPromise Then(ResultHandler success);
-        new IPromise Error(ErrorHandler error);
         IPromise Error(ErrorHandler error);
 
-        IPromise Map(ResultMapper mapper, ErrorHandler error);
+        IPromise Map(ResultMapper mapper, ErrorHandler error);
         IPromise Map(ResultMapper mapper);
 
-        IPromise Chain(ChainedOperation chained, ErrorHandler error);
+        IPromise Chain(ChainedOperation chained, ErrorHandler error);
         IPromise Chain(ChainedOperation chained);
 
         new IPromise Cancelled(Action handler);
diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj
--- a/Implab/Implab.csproj
+++ b/Implab/Implab.csproj
@@ -7,6 +7,8 @@
     Library
     Implab
     Implab
+    8.0.30703
+    2.0
   
   
     true
@@ -99,7 +101,79 @@
     
     
     
+    
+    
+    
   
   
   
+  
+    
+      
+        
+          
+          
+          
+          
+          
+          
+          
+            
+              
+              
+              
+                
+                  I
+                
+              
+              
+                
+                  Attribute
+                
+              
+              
+                
+                  EventArgs
+                
+              
+              
+                
+                  Exception
+                
+              
+              
+              
+              
+              
+              
+                
+                  m_
+                
+              
+              
+                
+                  _
+                
+              
+              
+                
+                  m_
+                
+              
+              
+              
+              
+              
+              
+              
+                
+                  T
+                
+              
+            
+          
+        
+      
+    
+  
 
\ No newline at end of file
diff --git a/Implab/JSON/JSONGrammar.cs b/Implab/JSON/JSONGrammar.cs
--- a/Implab/JSON/JSONGrammar.cs
+++ b/Implab/JSON/JSONGrammar.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace Implab.JSON {
     internal class JSONGrammar : Grammar {
-        public enum TokenType : int{
+        public enum TokenType : int {
             None,
             BeginObject,
             EndObject,
@@ -53,7 +53,6 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
                 .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);
diff --git a/Implab/JSON/JSONWriter.cs b/Implab/JSON/JSONWriter.cs
--- a/Implab/JSON/JSONWriter.cs
+++ b/Implab/JSON/JSONWriter.cs
@@ -15,7 +15,8 @@ namespace Implab.JSON {
         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 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
             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 @@ namespace Implab.JSON {
             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) {
diff --git a/Implab/Parallels/ArrayTraits.cs b/Implab/Parallels/ArrayTraits.cs
--- a/Implab/Parallels/ArrayTraits.cs
+++ b/Implab/Parallels/ArrayTraits.cs
@@ -174,7 +174,10 @@ namespace Implab.Parallels {
                                 if (left == 0)
                                     promise.Resolve(res);
                             },
-                            e => promise.Reject(e)
+                            e => {
+                                promise.Reject(e);
+                                throw new TransientPromiseException(e);
+                            }
                         );
 
                     } catch (Exception e) {
diff --git a/Implab/Promise.cs b/Implab/Promise.cs
--- a/Implab/Promise.cs
+++ b/Implab/Promise.cs
@@ -10,8 +10,8 @@ namespace Implab {
     public delegate void ErrorHandler(Exception e);
     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 TNew ResultMapper(TSrc result);
+    public delegate IPromise ChainedOperation(TSrc result);
 
     /// 
     /// Класс для асинхронного получения результатов. Так называемое "обещание".
@@ -51,32 +51,51 @@ namespace Implab {
 
         protected struct HandlerDescriptor {
             public ResultHandler resultHandler;
-            public ErrorHandler errorHandler;
+            public ErrorHandler errorHandler;
             public Action cancellHandler;
+            public Promise 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 @@ namespace Implab {
         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 @@ namespace Implab {
         /// 
         /// true Операция была отменена, обработчики не будут вызваны.false отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.
         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 @@ namespace Implab {
         /// 
         /// The handler of the successfully completed operation.
         /// This handler will recieve an operation result as a parameter.
-        /// Handles an exception that may occur during the operation.
-        /// The new promise chained to this one.
-        public IPromise Then(ResultHandler success, ErrorHandler error) {
-            if (success == null && error == null)
-                return this;
-
-            var medium = new Promise(this, true);
-
-            ResultHandler 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());
-        }
-
-        /// 
-        /// Adds new handlers to this promise.
-        /// 
-        /// The handler of the successfully completed operation.
-        /// This handler will recieve an operation result as a parameter.
         /// Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.
         /// The new promise chained to this one.
         public IPromise Then(ResultHandler success, ErrorHandler error) {
@@ -273,33 +238,25 @@ namespace Implab {
 
             var medium = new Promise(this, true);
 
-            ResultHandler 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 Then(ResultHandler success) {
             if (success == null)
@@ -307,23 +264,28 @@ namespace Implab {
 
             var medium = new Promise(this, true);
 
-            ResultHandler 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 Error(ErrorHandler error) {
-            return Then((ResultHandler)null, error);
+        public IPromise Error(ErrorHandler error) {
+            if (error == null)
+                return this;
+
+            var medium = new Promise(this, true);
+
+            AddHandler(
+                null,
+                e => {
+                    error(e);
+                    return default(T);
+                },
+                null,
+                medium
+            );
+
+            return medium;
         }
 
         /// 
@@ -340,17 +302,7 @@ namespace Implab {
 
             var medium = new Promise(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 @@ namespace Implab {
             if (handler == null)
                 return this;
 
-            var medium = new Promise(this,true);
+            var medium = new Promise(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 @@ namespace Implab {
         /// Обработчик ошибки. Данный обработчик получит
         /// исключение возникшее при выполнении операции.
         /// Новое обещание, которое будет выполнено при выполнении исходного обещания.
-        public IPromise Map(ResultMapper mapper, ErrorHandler error) {
+        public IPromise Map(ResultMapper mapper, ErrorHandler error) {
             if (mapper == null)
                 throw new ArgumentNullException("mapper");
 
             // создаем прицепленное обещание
-            var chained = new Promise(this,true);
+            var chained = new Promise(this, true);
 
             ResultHandler resultHandler = result => chained.Resolve(mapper(result));
-            ErrorHandler errorHandler = delegate(Exception e) {
-                if (error != null)
+            ErrorHandler 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 @@ namespace Implab {
         /// Обработчик ошибки. Данный обработчик получит
         /// исключение возникшее при выполнении текуещй операции.
         /// Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.
-        public IPromise Chain(ChainedOperation chained, ErrorHandler error) {
+        public IPromise Chain(ChainedOperation chained, ErrorHandler error) {
 
             // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
             // создать посредника, к которому будут подвызяваться следующие обработчики.
@@ -449,15 +399,18 @@ namespace Implab {
                 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 @@ namespace Implab {
                 promise.Cancelled(() => medium.Reject(new OperationCanceledException()));
             };
 
-            ErrorHandler errorHandler = delegate(Exception e) {
-                if (error != null)
-                    error(e);
+            ErrorHandler 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 @@ namespace Implab {
         }
 
         public IPromise Cancelled(Action handler) {
-            AddHandler(null, null, handler);
+            AddHandler(null, null, handler, null);
             return this;
         }
 
@@ -500,8 +461,12 @@ namespace Implab {
                 throw new ArgumentNullException("handler");
             AddHandler(
                 x => handler(),
-                e => handler(),
-                handler
+                e => {
+                    handler();
+                    throw new TransientPromiseException(e);
+                },
+                handler,
+                null
             );
             return this;
         }
@@ -560,14 +525,15 @@ namespace Implab {
             return Join(Timeout.Infinite);
         }
 
-        void AddHandler(ResultHandler success, ErrorHandler error, Action cancel) {
+        void AddHandler(ResultHandler success, ErrorHandler error, Action cancel, Promise 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 @@ namespace Implab {
                             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)
diff --git a/Implab/PromiseExtensions.cs b/Implab/PromiseExtensions.cs
new file mode 100644
--- /dev/null
+++ b/Implab/PromiseExtensions.cs
@@ -0,0 +1,38 @@
+using System.Threading;
+
+namespace Implab {
+    public static class PromiseExtensions {
+        public static IPromise DispatchToCurrentContext(this IPromise that) {
+            var context = SynchronizationContext.Current;
+            if (context == null)
+                return that;
+
+            var p = new SyncContextPromise(context, that, true);
+
+            that.Then(
+                x => p.Resolve(x),
+                e => {
+                    p.Reject(e);
+                    return default(T);
+                }
+            );
+            return p;
+        }
+
+        public static IPromise DispatchToContext(this IPromise that, SynchronizationContext context) {
+            Safe.ArgumentNotNull(context, "context");
+
+            var p = new SyncContextPromise(context, that, true);
+
+            that.Then(
+                x => p.Resolve(x),
+                e => {
+                    p.Reject(e);
+                    return default(T);
+                }
+            );
+            return p;
+        }
+    }
+}
+
diff --git a/Implab/SyncContextPromise.cs b/Implab/SyncContextPromise.cs
new file mode 100644
--- /dev/null
+++ b/Implab/SyncContextPromise.cs
@@ -0,0 +1,22 @@
+using System.Threading;
+
+namespace Implab {
+    public class SyncContextPromise : Promise {
+        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);
+        }
+    }
+}
+
diff --git a/Implab/TransientPromiseException.cs b/Implab/TransientPromiseException.cs
new file mode 100644
--- /dev/null
+++ b/Implab/TransientPromiseException.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Implab {
+
+    [Serializable]
+    public class TransientPromiseException : Exception {
+        /// 
+        /// Initializes a new instance of the  class.
+        /// 
+        /// The exception that is the cause of the current exception.
+        public TransientPromiseException(Exception inner) : base("The preceding promise has failed", inner) {
+        }
+
+        /// 
+        /// Initializes a new instance of the  class
+        /// 
+        /// A  that describes the exception. 
+        /// The exception that is the cause of the current exception. 
+        public TransientPromiseException(string message, Exception inner)
+            : base(message, inner) {
+        }
+
+        /// 
+        /// Initializes a new instance of the  class
+        /// 
+        /// The contextual information about the source or destination.
+        /// The object that holds the serialized object data.
+        protected TransientPromiseException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
+            : base(info, context) {
+        }
+    }
+}
+