├── .editorconfig
├── .gitattributes
├── .gitignore
├── .nuget
├── Microsoft.Build.dll
├── NuGet.Config
├── NuGet.exe
├── NuGet.mono.targets
└── NuGet.targets
├── .travis.yml
├── C-Sharp-Promise.sln
├── CONTRIBUTING.md
├── Examples
├── Example1
│ ├── App.config
│ ├── Example1.csproj
│ ├── Program.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
├── Example2
│ ├── App.config
│ ├── Example2.csproj
│ ├── Program.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
├── Example3
│ ├── App.config
│ ├── Example3.csproj
│ ├── Program.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
├── Example4
│ ├── App.config
│ ├── Example4.csproj
│ ├── Program.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
└── Example5
│ ├── App.config
│ ├── Example5.csproj
│ ├── Program.cs
│ └── Properties
│ └── AssemblyInfo.cs
├── LICENSE
├── README.md
├── Tests
├── A+ Spec
│ ├── 2.1.cs
│ └── 2.2.cs
├── Promise.Tests.csproj
├── PromiseProgressTests.cs
├── PromiseTests.cs
├── PromiseTimerTests.cs
├── Promise_NonGeneric_ProgressTests.cs
├── Promise_NonGeneric_Tests.cs
├── Properties
│ └── AssemblyInfo.cs
├── TestHelpers.cs
└── packages.config
├── packages
├── Moq.4.2.1409.1722
│ ├── Moq.4.2.1409.1722.nupkg
│ └── lib
│ │ ├── net35
│ │ ├── Moq.dll
│ │ └── Moq.xml
│ │ ├── net40
│ │ ├── Moq.dll
│ │ └── Moq.xml
│ │ └── sl4
│ │ ├── Moq.Silverlight.dll
│ │ └── Moq.Silverlight.xml
├── xunit.1.9.2
│ ├── lib
│ │ └── net20
│ │ │ ├── xunit.dll
│ │ │ ├── xunit.dll.tdnet
│ │ │ ├── xunit.runner.msbuild.dll
│ │ │ ├── xunit.runner.tdnet.dll
│ │ │ ├── xunit.runner.utility.dll
│ │ │ └── xunit.xml
│ └── xunit.1.9.2.nupkg
└── xunit.runner.visualstudio.0.99.9-build1021
│ ├── build
│ ├── _common
│ │ ├── xunit.abstractions.dll
│ │ ├── xunit.runner.utility.dll
│ │ ├── xunit.runner.visualstudio.testadapter.dll
│ │ └── xunit.runner.visualstudio.testadapter.pdb
│ ├── net20
│ │ └── xunit.runner.visualstudio.props
│ └── portable-net45+aspnetcore50+win+wpa81+wp80+monotouch+monoandroid
│ │ └── xunit.runner.visualstudio.props
│ ├── lib
│ ├── net20
│ │ └── _._
│ └── portable-net45+aspnetcore50+win+wpa81+wp80+monotouch+monoandroid
│ │ └── _._
│ └── xunit.runner.visualstudio.0.99.9-build1021.nupkg
├── src
├── C-Sharp-Promise.snk
├── EnumerableExt.cs
├── Exceptions
│ ├── PromiseException.cs
│ └── PromiseStateException.cs
├── Promise.cs
├── PromiseHelpers.cs
├── PromiseTimer.cs
├── Promise_NonGeneric.cs
├── RSG.Promise.csproj
└── Tuple.cs
└── xunit
├── xunit.console.clr4.x86.exe
└── xunit.runner.utility.dll
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | end_of_line = crlf
7 | insert_final_newline = false
8 | indent_style = space
9 | indent_size = 4
10 | charset = utf-8
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | .vs
4 |
5 | # User-specific files
6 | *.suo
7 | *.user
8 | *.sln.docstates
9 |
10 | # Build results
11 |
12 | [Dd]ebug/
13 | [Rr]elease/
14 | x64/
15 | build/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
20 | !packages/*/build/
21 |
22 | # MSTest test Results
23 | [Tt]est[Rr]esult*/
24 | [Bb]uild[Ll]og.*
25 |
26 | *_i.c
27 | *_p.c
28 | *.ilk
29 | *.meta
30 | *.obj
31 | *.pch
32 | *.pdb
33 | *.pgc
34 | *.pgd
35 | *.rsp
36 | *.sbr
37 | *.tlb
38 | *.tli
39 | *.tlh
40 | *.tmp
41 | *.tmp_proj
42 | *.log
43 | *.vspscc
44 | *.vssscc
45 | .builds
46 | *.pidb
47 | *.log
48 | *.scc
49 |
50 | # Visual C++ cache files
51 | ipch/
52 | *.aps
53 | *.ncb
54 | *.opensdf
55 | *.sdf
56 | *.cachefile
57 |
58 | # Visual Studio profiler
59 | *.psess
60 | *.vsp
61 | *.vspx
62 |
63 | # Guidance Automation Toolkit
64 | *.gpState
65 |
66 | # ReSharper is a .NET coding add-in
67 | _ReSharper*/
68 | *.[Rr]e[Ss]harper
69 |
70 | # TeamCity is a build add-in
71 | _TeamCity*
72 |
73 | # DotCover is a Code Coverage Tool
74 | *.dotCover
75 |
76 | # NCrunch
77 | *.ncrunch*
78 | .*crunch*.local.xml
79 |
80 | # Installshield output folder
81 | [Ee]xpress/
82 |
83 | # DocProject is a documentation generator add-in
84 | DocProject/buildhelp/
85 | DocProject/Help/*.HxT
86 | DocProject/Help/*.HxC
87 | DocProject/Help/*.hhc
88 | DocProject/Help/*.hhk
89 | DocProject/Help/*.hhp
90 | DocProject/Help/Html2
91 | DocProject/Help/html
92 |
93 | # Click-Once directory
94 | publish/
95 |
96 | # Publish Web Output
97 | *.Publish.xml
98 |
99 | # NuGet Packages Directory
100 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
101 | #packages/
102 |
103 | # Windows Azure Build Output
104 | csx
105 | *.build.csdef
106 |
107 | # Windows Store app package directory
108 | AppPackages/
109 |
110 | # Others
111 | sql/
112 | *.Cache
113 | ClientBin/
114 | [Ss]tyle[Cc]op.*
115 | ~$*
116 | *~
117 | *.dbmdl
118 | *.[Pp]ublish.xml
119 | *.pfx
120 | *.publishsettings
121 |
122 | # RIA/Silverlight projects
123 | Generated_Code/
124 |
125 | # Backup & report files from converting an old project file to a newer
126 | # Visual Studio version. Backup files are not needed, because we have git ;-)
127 | _UpgradeReport_Files/
128 | Backup*/
129 | UpgradeLog*.XML
130 | UpgradeLog*.htm
131 |
132 | # SQL Server files
133 | App_Data/*.mdf
134 | App_Data/*.ldf
135 |
136 |
137 | #LightSwitch generated files
138 | GeneratedArtifacts/
139 | _Pvt_Extensions/
140 | ModelManifest.xml
141 |
142 | #Ignore files generated by Rider IDE
143 | .idea/**
144 |
145 | # =========================
146 | # Windows detritus
147 | # =========================
148 |
149 | # Windows image file caches
150 | Thumbs.db
151 | ehthumbs.db
152 |
153 | # Folder config file
154 | Desktop.ini
155 |
156 | # Recycle Bin used on file shares
157 | $RECYCLE.BIN/
158 |
159 | # Mac desktop service store files
160 | .DS_Store
161 | packages/**/*.nupkg
162 | packages/**/*.nuspec
163 | *.nupkg
164 | packages/
--------------------------------------------------------------------------------
/.nuget/Microsoft.Build.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Real-Serious-Games/C-Sharp-Promise/824b714b47e40239aa6595a3288460c461539345/.nuget/Microsoft.Build.dll
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Real-Serious-Games/C-Sharp-Promise/824b714b47e40239aa6595a3288460c461539345/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.nuget/NuGet.mono.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 |
32 |
33 |
34 |
35 | $(SolutionDir).nuget
36 |
37 |
38 |
39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
41 |
42 |
43 |
44 | $(MSBuildProjectDirectory)\packages.config
45 | $(PackagesProjectConfig)
46 |
47 |
48 |
49 |
50 | $(NuGetToolsPath)\NuGet.exe
51 | @(PackageSource)
52 |
53 | "$(NuGetExePath)"
54 | mono --runtime=v4.0.30319 "$(NuGetExePath)"
55 |
56 | $(TargetDir.Trim('\\'))
57 |
58 | -RequireConsent
59 | -NonInteractive
60 |
61 | "$(SolutionDir)"
62 | "$(SolutionDir)"
63 |
64 |
65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
67 |
68 |
69 |
70 | RestorePackages;
71 | $(BuildDependsOn);
72 |
73 |
74 |
75 |
76 | $(BuildDependsOn);
77 | BuildPackage;
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 |
32 |
33 |
34 |
35 | $(SolutionDir).nuget
36 |
37 |
38 |
39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
41 |
42 |
43 |
44 | $(MSBuildProjectDirectory)\packages.config
45 | $(PackagesProjectConfig)
46 |
47 |
48 |
49 |
50 | $(NuGetToolsPath)\NuGet.exe
51 | @(PackageSource)
52 |
53 | "$(NuGetExePath)"
54 | mono --runtime=v4.0.30319 "$(NuGetExePath)"
55 |
56 | $(TargetDir.Trim('\\'))
57 |
58 | -RequireConsent
59 | -NonInteractive
60 |
61 | "$(SolutionDir) "
62 | "$(SolutionDir)"
63 |
64 |
65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
67 |
68 |
69 |
70 | RestorePackages;
71 | $(BuildDependsOn);
72 |
73 |
74 |
75 |
76 | $(BuildDependsOn);
77 | BuildPackage;
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: csharp
2 | solution: C-Sharp-Promise.sln
3 |
4 | install:
5 | - nuget restore C-Sharp-Promise.sln
6 | - nuget install xunit.runners -Version 1.9.2 -OutputDirectory testrunner
7 |
8 | script:
9 | - msbuild /p:Configuration=Debug C-Sharp-Promise.sln
10 | - mono ./testrunner/xunit.runners.1.9.2/tools/xunit.console.exe ./bin/Debug/RSG.Promise.Tests.dll
11 |
--------------------------------------------------------------------------------
/C-Sharp-Promise.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2042
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSG.Promise", "src\RSG.Promise.csproj", "{7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Promise.Tests", "Tests\Promise.Tests.csproj", "{4B4EE1A6-25A3-43D0-BAE9-5E45E31F2816}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{79A9B3AC-1942-4441-872C-41CF7BB240A4}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example1", "Examples\Example1\Example1.csproj", "{79A792E9-F378-47BB-A262-9E4E33B655DA}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2", "Examples\Example2\Example2.csproj", "{D98539EC-14C7-40B0-9ADA-D8378A1774C4}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example3", "Examples\Example3\Example3.csproj", "{6E70ADB8-B34B-4C71-BE15-3001F5C791B8}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example4", "Examples\Example4\Example4.csproj", "{BDB0135C-B075-47BE-A971-1927BEA6F622}"
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example5", "Examples\Example5\Example5.csproj", "{1060401E-1C17-4741-BF1E-9F05B5427775}"
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {4B4EE1A6-25A3-43D0-BAE9-5E45E31F2816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {4B4EE1A6-25A3-43D0-BAE9-5E45E31F2816}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {4B4EE1A6-25A3-43D0-BAE9-5E45E31F2816}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {4B4EE1A6-25A3-43D0-BAE9-5E45E31F2816}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {79A792E9-F378-47BB-A262-9E4E33B655DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {79A792E9-F378-47BB-A262-9E4E33B655DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {79A792E9-F378-47BB-A262-9E4E33B655DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {79A792E9-F378-47BB-A262-9E4E33B655DA}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {D98539EC-14C7-40B0-9ADA-D8378A1774C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {D98539EC-14C7-40B0-9ADA-D8378A1774C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {D98539EC-14C7-40B0-9ADA-D8378A1774C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {D98539EC-14C7-40B0-9ADA-D8378A1774C4}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {6E70ADB8-B34B-4C71-BE15-3001F5C791B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {6E70ADB8-B34B-4C71-BE15-3001F5C791B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {6E70ADB8-B34B-4C71-BE15-3001F5C791B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {6E70ADB8-B34B-4C71-BE15-3001F5C791B8}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {BDB0135C-B075-47BE-A971-1927BEA6F622}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {BDB0135C-B075-47BE-A971-1927BEA6F622}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {BDB0135C-B075-47BE-A971-1927BEA6F622}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {BDB0135C-B075-47BE-A971-1927BEA6F622}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {1060401E-1C17-4741-BF1E-9F05B5427775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {1060401E-1C17-4741-BF1E-9F05B5427775}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {1060401E-1C17-4741-BF1E-9F05B5427775}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {1060401E-1C17-4741-BF1E-9F05B5427775}.Release|Any CPU.Build.0 = Release|Any CPU
56 | EndGlobalSection
57 | GlobalSection(SolutionProperties) = preSolution
58 | HideSolutionNode = FALSE
59 | EndGlobalSection
60 | GlobalSection(NestedProjects) = preSolution
61 | {79A792E9-F378-47BB-A262-9E4E33B655DA} = {79A9B3AC-1942-4441-872C-41CF7BB240A4}
62 | {D98539EC-14C7-40B0-9ADA-D8378A1774C4} = {79A9B3AC-1942-4441-872C-41CF7BB240A4}
63 | {6E70ADB8-B34B-4C71-BE15-3001F5C791B8} = {79A9B3AC-1942-4441-872C-41CF7BB240A4}
64 | {BDB0135C-B075-47BE-A971-1927BEA6F622} = {79A9B3AC-1942-4441-872C-41CF7BB240A4}
65 | {1060401E-1C17-4741-BF1E-9F05B5427775} = {79A9B3AC-1942-4441-872C-41CF7BB240A4}
66 | EndGlobalSection
67 | GlobalSection(ExtensibilityGlobals) = postSolution
68 | SolutionGuid = {7D2FBA48-64F2-4236-BA93-0AF605D743D8}
69 | EndGlobalSection
70 | EndGlobal
71 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute to C-Sharp-Promise
2 |
3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍
4 |
5 |
6 | ## Reporting bugs
7 |
8 | Any bug reports are useful, although there are a few things you can do that will
9 | make it easier for maintainers and other users to understand your report,
10 | reproduce the behaviour, and find related reports.
11 |
12 | - **Use a clear and descriptive title** for the issue to identify the problem.
13 | - **Describe the exact steps which reproduce the problem** in as many details
14 | as possible. Code examples or links to repos to reproduce the issue are
15 | always helpful.
16 | - **Describe the behaviour you observed after following the steps** and point
17 | out what exactly the problem is with that behaviour.
18 | - **Explain which behaviour you expected to see instead and why.** Since
19 | C-Sharp-Promise is designed to replicate [JavaScript promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises),
20 | code that works with JavaScript promises but gives a different result or
21 | breaks with C-Sharp-Promise would be a a good example.
22 | - **Check the existing open issues on GitHub** to see if someone else has had
23 | the same problem as you.
24 |
25 | Make sure to file bugs as [GitHub issues](https://github.com/Real-Serious-Games/C-Sharp-Promise/issues),
26 | since that way everyone working on the library can see it and potentially help.
27 |
28 |
29 | ## Pull requests
30 |
31 | Before we merge pull requests there are a few things we look for to make sure
32 | the code is maintainable and up the same standard as the rest of the library.
33 |
34 | - Make sure you've written comprehensive unit tests for the feature, or
35 | modify existing tests if the feature changes functionality.
36 | - Check that your code conforms to the same style as existing code. Ensure that
37 | your editor is set up to read the [.editorconfig](http://editorconfig.org/)
38 | file for consistent spacing and line endings. We also try to keep our code
39 | style consistent with the [Microsoft C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions)
40 | and [Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/index).
41 | - Make sure that the [Travis CI build](https://travis-ci.org/Real-Serious-Games/C-Sharp-Promise)
42 | succeeds. This should run automatically when you create a pull request, but
43 | should have the same result as building the solution and running all the
44 | tests locally. We will not accept any pull requests that fail to build or
45 | contain failing tests.
46 | - If you have added a new feature, add a section to README.md describing the
47 | feature and how to use it.
48 |
49 | In addition, if your pull request breaks any existing functionality it's
50 | unlikely we will merge it unless there is a very good reason.
--------------------------------------------------------------------------------
/Examples/Example1/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Examples/Example1/Example1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {79A792E9-F378-47BB-A262-9E4E33B655DA}
8 | Exe
9 | Properties
10 | Example
11 | Example
12 | v3.5
13 | 512
14 |
15 | ..\..\
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3}
55 | RSG.Promise
56 |
57 |
58 |
59 |
60 |
61 |
62 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/Examples/Example1/Program.cs:
--------------------------------------------------------------------------------
1 | using RSG;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading;
8 |
9 | //
10 | // Example of downloading text from a URL using a promise.
11 | //
12 | namespace Example
13 | {
14 | class Program
15 | {
16 | static void Main(string[] args)
17 | {
18 | var running = true;
19 |
20 | Download("http://www.google.com") // Schedule an async operation.
21 | .Then(result => // Use Done to register a callback to handle completion of the async operation.
22 | {
23 | Console.WriteLine("Async operation completed.");
24 | Console.WriteLine(result.Substring(0, 250) + "...");
25 | running = false;
26 | })
27 | .Done();
28 |
29 | Console.WriteLine("Waiting");
30 |
31 | while (running)
32 | {
33 | Thread.Sleep(10);
34 | }
35 |
36 | Console.WriteLine("Exiting");
37 | }
38 |
39 | ///
40 | /// Download text from a URL.
41 | /// A promise is returned that is resolved when the download has completed.
42 | /// The promise is rejected if an error occurs during download.
43 | ///
44 | static IPromise Download(string url)
45 | {
46 | Console.WriteLine("Downloading " + url + " ...");
47 |
48 | var promise = new Promise();
49 | using (var client = new WebClient())
50 | {
51 | client.DownloadStringCompleted +=
52 | (s, ev) =>
53 | {
54 | if (ev.Error != null)
55 | {
56 | Console.WriteLine("An error occurred... rejecting the promise.");
57 |
58 | // Error during download, reject the promise.
59 | promise.Reject(ev.Error);
60 | }
61 | else
62 | {
63 | Console.WriteLine("... Download completed.");
64 |
65 | // Downloaded completed successfully, resolve the promise.
66 | promise.Resolve(ev.Result);
67 | }
68 | };
69 |
70 | client.DownloadStringAsync(new Uri(url), null);
71 | }
72 | return promise;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Examples/Example1/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Example")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Real Serious Games")]
12 | [assembly: AssemblyProduct("Example")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("44f124da-f58f-4c64-94a2-30acaeea3111")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Examples/Example2/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Examples/Example2/Example2.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D98539EC-14C7-40B0-9ADA-D8378A1774C4}
8 | Exe
9 | Properties
10 | Example
11 | Example
12 | v3.5
13 | 512
14 |
15 | ..\..\
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3}
55 | RSG.Promise
56 |
57 |
58 |
59 |
60 |
61 |
62 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/Examples/Example2/Program.cs:
--------------------------------------------------------------------------------
1 | using RSG;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading;
8 |
9 | //
10 | // Example of a promise that is rejected because of an error during the async operation.
11 | //
12 | namespace Example
13 | {
14 | class Program
15 | {
16 | static void Main(string[] args)
17 | {
18 | var running = true;
19 |
20 | Download("http://www.bugglebogglebazzooo.com") // Schedule async operation, this time the URL is bad!
21 | .Catch(exception =>
22 | {
23 | Console.WriteLine("Async operation errorred.");
24 | Console.WriteLine(exception);
25 | running = false;
26 | });
27 |
28 | Console.WriteLine("Waiting");
29 |
30 | while (running)
31 | {
32 | Thread.Sleep(10);
33 | }
34 |
35 | Console.WriteLine("Exiting");
36 | }
37 |
38 | ///
39 | /// Download text from a URL.
40 | /// A promise is returned that is resolved when the download has completed.
41 | /// The promise is rejected if an error occurs during download.
42 | ///
43 | static IPromise Download(string url)
44 | {
45 | Console.WriteLine("Downloading " + url + " ...");
46 |
47 | var promise = new Promise();
48 | using (var client = new WebClient())
49 | {
50 | client.DownloadStringCompleted +=
51 | (s, ev) =>
52 | {
53 | if (ev.Error != null)
54 | {
55 | Console.WriteLine("An error occurred... rejecting the promise.");
56 |
57 | // Error during download, reject the promise.
58 | promise.Reject(ev.Error);
59 | }
60 | else
61 | {
62 | Console.WriteLine("... Download completed.");
63 |
64 | // Downloaded completed successfully, resolve the promise.
65 | promise.Resolve(ev.Result);
66 | }
67 | };
68 |
69 | client.DownloadStringAsync(new Uri(url), null);
70 | }
71 | return promise;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Examples/Example2/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Example")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Real Serious Games")]
12 | [assembly: AssemblyProduct("Example")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("44f124da-f58f-4c64-94a2-30acaeea3111")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Examples/Example3/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Examples/Example3/Example3.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6E70ADB8-B34B-4C71-BE15-3001F5C791B8}
8 | Exe
9 | Properties
10 | Example
11 | Example
12 | v3.5
13 | 512
14 |
15 | ..\..\
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3}
55 | RSG.Promise
56 |
57 |
58 |
59 |
60 |
61 |
62 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/Examples/Example3/Program.cs:
--------------------------------------------------------------------------------
1 | using RSG;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using System.Threading;
9 |
10 | //
11 | // This example downloads search results from google then transforms the result to extract links.
12 | // It includes both error handling and a completion handler.
13 | //
14 | namespace Example
15 | {
16 | class Program
17 | {
18 | //
19 | // URL for a google search on 'promises'.
20 | //
21 | static string searchUrl = "https://www.google.com/#q=promises";
22 |
23 | static void Main(string[] args)
24 | {
25 | var running = true;
26 |
27 | Download(searchUrl) // Invoke a google search.
28 | .Then(html => // Transforms search results and extract links.
29 | {
30 | return LinkFinder
31 | .Find(html)
32 | .Select(link => link.Href)
33 | .ToArray();
34 | })
35 | .Then(links => // Display the links that were extracted.
36 | {
37 | Console.WriteLine("Async operation completed.");
38 | foreach (var link in links)
39 | {
40 | Console.WriteLine(link);
41 | }
42 | running = false;
43 | })
44 | .Catch(exception => // Catch any errors that happen during download or transform.
45 | {
46 | Console.WriteLine("Async operation errorred.");
47 | Console.WriteLine(exception);
48 | running = false;
49 | })
50 | .Done();
51 |
52 | Console.WriteLine("Waiting");
53 |
54 | while (running)
55 | {
56 | Thread.Sleep(10);
57 | }
58 |
59 | Console.WriteLine("Exiting");
60 | }
61 |
62 | ///
63 | /// Download text from a URL.
64 | /// A promise is returned that is resolved when the download has completed.
65 | /// The promise is rejected if an error occurs during download.
66 | ///
67 | static IPromise Download(string url)
68 | {
69 | Console.WriteLine("Downloading " + url + " ...");
70 |
71 | var promise = new Promise();
72 | using (var client = new WebClient())
73 | {
74 | client.DownloadStringCompleted +=
75 | (s, ev) =>
76 | {
77 | if (ev.Error != null)
78 | {
79 | Console.WriteLine("An error occurred... rejecting the promise.");
80 |
81 | // Error during download, reject the promise.
82 | promise.Reject(ev.Error);
83 | }
84 | else
85 | {
86 | Console.WriteLine("... Download completed.");
87 |
88 | // Downloaded completed successfully, resolve the promise.
89 | promise.Resolve(ev.Result);
90 | }
91 | };
92 |
93 | client.DownloadStringAsync(new Uri(url), null);
94 | }
95 | return promise;
96 | }
97 |
98 | //
99 | // LinkItem and LinkFinder from this site:
100 | //
101 | // http://www.dotnetperls.com/scraping-html
102 | //
103 |
104 | public struct LinkItem
105 | {
106 | public string Href;
107 | public string Text;
108 |
109 | public override string ToString()
110 | {
111 | return Href + "\n\t" + Text;
112 | }
113 | }
114 |
115 | static class LinkFinder
116 | {
117 | public static List Find(string file)
118 | {
119 | List list = new List();
120 |
121 | // 1.
122 | // Find all matches in file.
123 | MatchCollection m1 = Regex.Matches(file, @"(.*?)",
124 | RegexOptions.Singleline);
125 |
126 | // 2.
127 | // Loop over each match.
128 | foreach (Match m in m1)
129 | {
130 | string value = m.Groups[1].Value;
131 | LinkItem i = new LinkItem();
132 |
133 | // 3.
134 | // Get href attribute.
135 | Match m2 = Regex.Match(value, @"href=\""(.*?)\""",
136 | RegexOptions.Singleline);
137 | if (m2.Success)
138 | {
139 | i.Href = m2.Groups[1].Value;
140 | }
141 |
142 | // 4.
143 | // Remove inner tags from text.
144 | string t = Regex.Replace(value, @"\s*<.*?>\s*", "",
145 | RegexOptions.Singleline);
146 | i.Text = t;
147 |
148 | list.Add(i);
149 | }
150 | return list;
151 | }
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/Examples/Example3/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Example")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Real Serious Games")]
12 | [assembly: AssemblyProduct("Example")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("44f124da-f58f-4c64-94a2-30acaeea3111")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Examples/Example4/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Examples/Example4/Example4.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {BDB0135C-B075-47BE-A971-1927BEA6F622}
8 | Exe
9 | Properties
10 | Example
11 | Example
12 | v3.5
13 | 512
14 |
15 | ..\..\
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3}
55 | RSG.Promise
56 |
57 |
58 |
59 |
60 |
61 |
62 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/Examples/Example4/Program.cs:
--------------------------------------------------------------------------------
1 | using RSG;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using System.Threading;
9 |
10 | //
11 | // This example downloads search results from google, extracts the links and follows only a single first link, downloads its then prints the result.
12 | // It includes both error handling and a completion handler.
13 | //
14 | namespace Example4
15 | {
16 | class Program
17 | {
18 | //
19 | // URL for a google search on 'promises'.
20 | //
21 | static string searchUrl = "https://www.google.com/#q=promises";
22 |
23 | static void Main(string[] args)
24 | {
25 | var running = true;
26 |
27 | Download(searchUrl) // Invoke a google search.
28 | .Then(html => // Transforms search results and extract links.
29 | {
30 | return LinkFinder
31 | .Find(html)
32 | .Select(link => link.Href)
33 | .Skip(5)
34 | .First(); // Grab the 6th link.
35 | })
36 | .Then(firstLink => Download(firstLink)) // Follow the first link and download it.
37 | .Then(html => // Display html from the link that was followed.
38 | {
39 | Console.WriteLine("Async operation completed.");
40 | Console.WriteLine(html.Substring(0, 250) + "...");
41 | running = false;
42 | })
43 | .Catch(exception => // Catch any errors that happen during download or transform.
44 | {
45 | Console.WriteLine("Async operation errorred.");
46 | Console.WriteLine(exception);
47 | running = false;
48 | })
49 | .Done();
50 |
51 | Console.WriteLine("Waiting");
52 |
53 | while (running)
54 | {
55 | Thread.Sleep(10);
56 | }
57 |
58 | Console.WriteLine("Exiting");
59 | }
60 |
61 | ///
62 | /// Download text from a URL.
63 | /// A promise is returned that is resolved when the download has completed.
64 | /// The promise is rejected if an error occurs during download.
65 | ///
66 | static IPromise Download(string url)
67 | {
68 | Console.WriteLine("Downloading " + url + " ...");
69 |
70 | var promise = new Promise();
71 | using (var client = new WebClient())
72 | {
73 | client.DownloadStringCompleted +=
74 | (s, ev) =>
75 | {
76 | if (ev.Error != null)
77 | {
78 | Console.WriteLine("An error occurred... rejecting the promise.");
79 |
80 | // Error during download, reject the promise.
81 | promise.Reject(ev.Error);
82 | }
83 | else
84 | {
85 | Console.WriteLine("... Download completed.");
86 |
87 | // Downloaded completed successfully, resolve the promise.
88 | promise.Resolve(ev.Result);
89 | }
90 | };
91 |
92 | client.DownloadStringAsync(new Uri(url), null);
93 | }
94 | return promise;
95 | }
96 |
97 | //
98 | // LinkItem and LinkFinder from this site:
99 | //
100 | // http://www.dotnetperls.com/scraping-html
101 | //
102 |
103 | public struct LinkItem
104 | {
105 | public string Href;
106 | public string Text;
107 |
108 | public override string ToString()
109 | {
110 | return Href + "\n\t" + Text;
111 | }
112 | }
113 |
114 | static class LinkFinder
115 | {
116 | public static List Find(string file)
117 | {
118 | List list = new List();
119 |
120 | // 1.
121 | // Find all matches in file.
122 | MatchCollection m1 = Regex.Matches(file, @"(.*?)",
123 | RegexOptions.Singleline);
124 |
125 | // 2.
126 | // Loop over each match.
127 | foreach (Match m in m1)
128 | {
129 | string value = m.Groups[1].Value;
130 | LinkItem i = new LinkItem();
131 |
132 | // 3.
133 | // Get href attribute.
134 | Match m2 = Regex.Match(value, @"href=\""(.*?)\""",
135 | RegexOptions.Singleline);
136 | if (m2.Success)
137 | {
138 | i.Href = m2.Groups[1].Value;
139 | }
140 |
141 | // 4.
142 | // Remove inner tags from text.
143 | string t = Regex.Replace(value, @"\s*<.*?>\s*", "",
144 | RegexOptions.Singleline);
145 | i.Text = t;
146 |
147 | list.Add(i);
148 | }
149 | return list;
150 | }
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/Examples/Example4/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Example")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Real Serious Games")]
12 | [assembly: AssemblyProduct("Example")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("44f124da-f58f-4c64-94a2-30acaeea3111")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Examples/Example5/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Examples/Example5/Example5.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1060401E-1C17-4741-BF1E-9F05B5427775}
8 | Exe
9 | Properties
10 | Example
11 | Example
12 | v3.5
13 | 512
14 |
15 | ..\..\
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3}
55 | RSG.Promise
56 |
57 |
58 |
59 |
60 |
61 |
62 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/Examples/Example5/Program.cs:
--------------------------------------------------------------------------------
1 | using RSG;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using System.Threading;
9 |
10 | //
11 | // This example downloads search results from google, extracts the links, follows all (absolute) links and combines all async operations in a single operation using the All function.
12 | //
13 | namespace Example4
14 | {
15 | class Program
16 | {
17 | //
18 | // URL for a google search on 'promises'.
19 | //
20 | static string searchUrl = "https://www.google.com/#q=promises";
21 |
22 | static void Main(string[] args)
23 | {
24 | var running = true;
25 |
26 | Download(searchUrl) // Invoke a google search.
27 | .Then(html => // Transforms search results and extract links.
28 | {
29 | return LinkFinder
30 | .Find(html)
31 | .Select(link => link.Href);
32 | })
33 | // Download all extracted links.
34 | .Then(links => Promise.All( // Combine multiple promises into a single async operation.
35 | links
36 | .Where(link => link.StartsWith("http")) // Filter out relative links.
37 | .Select( // Convert collection of links to a collection of promises that are downloading the links.
38 | link => Download(link) // Download each link.
39 | )
40 | ))
41 | .Then(htmls => // Display html from the link that was followed.
42 | {
43 | Console.WriteLine("Async operation completed.");
44 |
45 | foreach (var html in htmls)
46 | {
47 | Console.WriteLine("---------------");
48 | Console.WriteLine(html.Substring(0, 250) + "...");
49 | }
50 |
51 | Console.WriteLine("---------------");
52 | Console.WriteLine("Downloaded " + htmls.Count() + " pages");
53 |
54 | running = false;
55 | })
56 | .Catch(exception => // Catch any errors that happen during download or transform.
57 | {
58 | Console.WriteLine("Async operation errorred.");
59 | Console.WriteLine(exception);
60 | running = false;
61 | })
62 | .Done();
63 |
64 | Console.WriteLine("Waiting");
65 |
66 | while (running)
67 | {
68 | Thread.Sleep(10);
69 | }
70 |
71 | Console.WriteLine("Exiting");
72 | }
73 |
74 | ///
75 | /// Download text from a URL.
76 | /// A promise is returned that is resolved when the download has completed.
77 | /// The promise is rejected if an error occurs during download.
78 | ///
79 | static IPromise Download(string url)
80 | {
81 | Console.WriteLine("Downloading " + url + " ...");
82 |
83 | var promise = new Promise();
84 | using (var client = new WebClient())
85 | {
86 | client.DownloadStringCompleted +=
87 | (s, ev) =>
88 | {
89 | if (ev.Error != null)
90 | {
91 | Console.WriteLine("An error occurred... rejecting the promise.");
92 |
93 | // Error during download, reject the promise.
94 | promise.Reject(ev.Error);
95 | }
96 | else
97 | {
98 | Console.WriteLine("... Download completed.");
99 |
100 | // Downloaded completed successfully, resolve the promise.
101 | promise.Resolve(ev.Result);
102 | }
103 | };
104 |
105 | client.DownloadStringAsync(new Uri(url), null);
106 | }
107 | return promise;
108 | }
109 |
110 | //
111 | // LinkItem and LinkFinder from this site:
112 | //
113 | // http://www.dotnetperls.com/scraping-html
114 | //
115 |
116 | public struct LinkItem
117 | {
118 | public string Href;
119 | public string Text;
120 |
121 | public override string ToString()
122 | {
123 | return Href + "\n\t" + Text;
124 | }
125 | }
126 |
127 | static class LinkFinder
128 | {
129 | public static List Find(string file)
130 | {
131 | List list = new List();
132 |
133 | // 1.
134 | // Find all matches in file.
135 | MatchCollection m1 = Regex.Matches(file, @"(.*?)",
136 | RegexOptions.Singleline);
137 |
138 | // 2.
139 | // Loop over each match.
140 | foreach (Match m in m1)
141 | {
142 | string value = m.Groups[1].Value;
143 | LinkItem i = new LinkItem();
144 |
145 | // 3.
146 | // Get href attribute.
147 | Match m2 = Regex.Match(value, @"href=\""(.*?)\""",
148 | RegexOptions.Singleline);
149 | if (m2.Success)
150 | {
151 | i.Href = m2.Groups[1].Value;
152 | }
153 |
154 | // 4.
155 | // Remove inner tags from text.
156 | string t = Regex.Replace(value, @"\s*<.*?>\s*", "",
157 | RegexOptions.Singleline);
158 | i.Text = t;
159 |
160 | list.Add(i);
161 | }
162 | return list;
163 | }
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/Examples/Example5/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Example")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Real Serious Games")]
12 | [assembly: AssemblyProduct("Example")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("44f124da-f58f-4c64-94a2-30acaeea3111")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Real Serious Games
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # C-Sharp-Promise [](https://travis-ci.org/Real-Serious-Games/C-Sharp-Promise) [](https://www.nuget.org/packages/RSG.Promise/) #
2 |
3 |
4 |
6 |
7 |
8 | Promises library for C# for management of asynchronous operations.
9 |
10 | Inspired by JavaScript promises, but slightly different.
11 |
12 | Used by [Real Serious Games](https://github.com/Real-Serious-Games/C-Sharp-Promise) for building serious games and simulations on Unity3d.
13 |
14 | If you are interested in using promises for game development and Unity please see [this article](http://www.what-could-possibly-go-wrong.com/promises-for-game-development/).
15 |
16 | ## Recent Updates
17 |
18 | - v3.0 (15 Feburary 2018)
19 | - *Finally* has been modified to work in a way consistent to [Promise.prototype.finally()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally) in JavaScript.
20 | - Added support for reporting progress in a promise.
21 | - Signed assembly with a strong name.
22 | - Errors throw custom exception types rather than generic ones.
23 | - Modified some overloads of *Then* that didn't make sense.
24 | - v2.0 (4 December 2017)
25 | - *Then* functions chained after a *Catch* are now run after the exception is handled rather than being terminated
26 | - *Catch* can return a value which will be passed into the next *Then*
27 | - The *onResolved* callback of *Then* can now also return a value which is passed to the next promise in the same way
28 | - Added *elapsedUpdates* property to the TimeData struct used by PromiseTimer
29 | - v1.3 (28 October 2017)
30 | - Added Cancel method to PromiseTimer
31 | - Implemented an overload of Promise.All that works on Tuples of multiple types
32 | - Implemented Finally method
33 | - Removed dependency on RSG.Toolkit
34 | - v1.2 (8 March 2015)
35 | - *Transform* function has been renamed to *Then* (another overload of *Then*).
36 |
37 | ## Projects using this library
38 | - **[RestClient for Unity 🤘](https://github.com/proyecto26/RestClient)**
39 |
40 | ## Contents
41 |
42 |
43 |
44 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
45 |
46 | - [Understanding Promises](#understanding-promises)
47 | - [Promises/A+ Spec](#promisesa-spec)
48 | - [Getting the DLL](#getting-the-dll)
49 | - [Getting the Code](#getting-the-code)
50 | - [Creating a Promise for an Async Operation](#creating-a-promise-for-an-async-operation)
51 | - [Creating a Promise, Alternate Method](#creating-a-promise-alternate-method)
52 | - [Waiting for an Async Operation to Complete](#waiting-for-an-async-operation-to-complete)
53 | - [Chaining Async Operations](#chaining-async-operations)
54 | - [Transforming the Results](#transforming-the-results)
55 | - [Error Handling](#error-handling)
56 | - [Unhandled Errors](#unhandled-errors)
57 | - [Progress reporting](#progress-reporting)
58 | - [Promises that are already Resolved/Rejected](#promises-that-are-already-resolvedrejected)
59 | - [Interfaces](#interfaces)
60 | - [Combining Multiple Async Operations](#combining-multiple-async-operations)
61 | - [Chaining Multiple Async Operations](#chaining-multiple-async-operations)
62 | - [Racing Asynchronous Operations](#racing-asynchronous-operations)
63 | - [Chaining Synchronous Actions that have no Result](#chaining-synchronous-actions-that-have-no-result)
64 | - [Promises that have no Results (a non-value promise)](#promises-that-have-no-results-a-non-value-promise)
65 | - [Convert a value promise to a non-value promise](#convert-a-value-promise-to-a-non-value-promise)
66 | - [Running a Sequence of Operations](#running-a-sequence-of-operations)
67 | - [Combining Parallel and Sequential Operations](#combining-parallel-and-sequential-operations)
68 | - [Weighted averaging of progress on multiple promises](#weighted-averaging-of-progress-on-multiple-promises)
69 | - [PromiseTimer class](#promisetimer-class)
70 | - [PromiseTimer.WaitFor](#promisetimerwaitfor)
71 | - [PromiseTimer.WaitUntil](#promisetimerwaituntil)
72 | - [PromiseTimer.WaitWhile](#promisetimerwaitwhile)
73 | - [TimeData struct](#timedata-struct)
74 | - [Examples](#examples)
75 |
76 |
77 |
78 | ## Understanding Promises
79 |
80 | To learn about promises:
81 |
82 | - [Promises on Wikpedia](http://en.wikipedia.org/wiki/Futures_and_promises)
83 | - [Good overview](https://www.promisejs.org/)
84 | - [Mozilla](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)
85 |
86 | ## Promises/A+ Spec
87 |
88 | This promise library conforms to the [Promises/A+ Spec](https://promisesaplus.com/) (at least, as far as is possible with C#):
89 |
90 | ## Getting the DLL
91 |
92 | The DLL can be installed via nuget. Use the Package Manager UI or console in Visual Studio or use nuget from the command line.
93 |
94 | See here for instructions on installing a package via nuget: http://docs.nuget.org/docs/start-here/using-the-package-manager-console
95 |
96 | The package to search for is *RSG.Promise*.
97 |
98 | ## Getting the Code
99 |
100 | You can get the code by cloning the github repository. You can do this in a UI like SourceTree or you can do it from the command line as follows:
101 |
102 | git clone https://github.com/Real-Serious-Games/C-Sharp-Promise.git
103 |
104 | Alternately, to contribute please fork the project in github.
105 |
106 | ## Creating a Promise for an Async Operation ##
107 |
108 | Reference the DLL and import the namespace:
109 | ```cs
110 | using RSG;
111 | ```
112 | Create a promise before you start the async operation:
113 | ```cs
114 | var promise = new Promise();
115 | ```
116 | The type of the promise should reflect the result of the async op.
117 |
118 | Then initiate your async operation and return the promise to the caller.
119 |
120 | Upon completion of the async op the promise is resolved:
121 | ```cs
122 | promise.Resolve(myValue);
123 | ```
124 | The promise is rejected on error/exception:
125 | ```cs
126 | promise.Reject(myException);
127 | ```
128 | To see it in context, here is an example function that downloads text from a URL. The promise is resolved when the download completes. If there is an error during download, say *unresolved domain name*, then the promise is rejected:
129 |
130 | ```cs
131 | public IPromise Download(string url)
132 | {
133 | var promise = new Promise(); // Create promise.
134 | using (var client = new WebClient())
135 | {
136 | client.DownloadStringCompleted += // Monitor event for download completed.
137 | (s, ev) =>
138 | {
139 | if (ev.Error != null)
140 | {
141 | promise.Reject(ev.Error); // Error during download, reject the promise.
142 | }
143 | else
144 | {
145 | promise.Resolve(ev.Result); // Downloaded completed successfully, resolve the promise.
146 | }
147 | };
148 |
149 | client.DownloadStringAsync(new Uri(url), null); // Initiate async op.
150 | }
151 |
152 | return promise; // Return the promise so the caller can await resolution (or error).
153 | }
154 | ```
155 |
156 | ## Creating a Promise, Alternate Method
157 |
158 | There is another way to create a promise that replicates the JavaScript convention of passing a *resolver* function into the constructor. The resolver function is passed functions that resolve or reject the promise. This allows you to express the previous example like this:
159 |
160 | ```cs
161 | var promise = new Promise((resolve, reject) =>
162 | {
163 | using (var client = new WebClient())
164 | {
165 | client.DownloadStringCompleted += // Monitor event for download completed.
166 | (s, ev) =>
167 | {
168 | if (ev.Error != null)
169 | {
170 | reject(ev.Error); // Error during download, reject the promise.
171 | }
172 | else
173 | {
174 | resolve(ev.Result); // Downloaded completed successfully, resolve the promise.
175 | }
176 | };
177 |
178 | client.DownloadStringAsync(new Uri(url), null); // Initiate async op.
179 | }
180 | });
181 | ```
182 |
183 | ## Waiting for an Async Operation to Complete ##
184 |
185 | The simplest usage is to register a completion handler to be invoked on completion of the async op:
186 | ```cs
187 | Download("http://www.google.com")
188 | .Then(html =>
189 | Console.WriteLine(html)
190 | );
191 | ```
192 |
193 | This snippet downloads the front page from Google and prints it to the console.
194 |
195 | For all but the most trivial applications you will also want to register an error hander:
196 | ```cs
197 | Download("http://www.google.com")
198 | .Then(html =>
199 | Console.WriteLine(html)
200 | )
201 | .Catch(exception =>
202 | Console.WriteLine("An exception occured while downloading!")
203 | );
204 | ```
205 |
206 | The chain of processing for a promise ends as soon as an error/exception occurs. In this case when an error occurs the *Catch* handler would be called, but not the *Done* handler. If there is no error, then only *Done* is called.
207 |
208 | ## Chaining Async Operations ##
209 |
210 | Multiple async operations can be chained one after the other using *Then*:
211 | ```cs
212 | Download("http://www.google.com")
213 | .Then(html =>
214 | return Download(ExtractFirstLink(html)) // Extract the first link and download it.
215 | )
216 | .Then(firstLinkHtml =>
217 | Console.WriteLine(firstLinkHtml)
218 | )
219 | .Catch(exception =>
220 | Console.WriteLine("An exception occured while downloading!")
221 | );
222 | ```
223 |
224 | Here we are chaining another download onto the end of the first download. The first link in the html is extracted and we then download that. *Then* expects the return value to be another promise. The chained promise can have a different *result type*.
225 |
226 | ## Transforming the Results ##
227 |
228 | Sometimes you will want to simply transform or modify the resulting value without chaining another async operation.
229 | ```cs
230 | Download("http://www.google.com")
231 | .Then(html =>
232 | return ExtractAllLinks(html)) // Extract all links and return an array of strings.
233 | )
234 | .Then(links => // The input here is an array of strings.
235 | foreach (var link in links)
236 | {
237 | Console.WriteLine(link);
238 | }
239 | );
240 | ```
241 |
242 | As is demonstrated the type of the value can also be changed during transformation. In the previous snippet a `Promise` is transformed to a `Promise`.
243 |
244 | ## Error Handling
245 |
246 | An error raised in a callback aborts the function and all subsequent callbacks in the chain:
247 | ```cs
248 | promise.Then(v => Something()) // <--- An error here aborts all subsequent callbacks...
249 | .Then(v => SomethingElse())
250 | .Then(v => AnotherThing())
251 | .Catch(e => HandleError(e)) // <--- Until the error handler is invoked here.
252 | ```
253 |
254 | ## Unhandled Errors
255 |
256 | When `Catch` is omitted exceptions go silently unhandled. This is an acknowledged issue with the Promises pattern.
257 |
258 | We handle this in a similar way to the JavaScript [Q](http://documentup.com/kriskowal/q) library. The `Done` method is used to terminate a chain, it registers a default catch handler that propagates unhandled exceptions to a default error handling mechanism that can be hooked into by the user.
259 |
260 | Terminating a Promise chain using `Done`:
261 | ```cs
262 | promise.Then(v => Something())
263 | .Then(v => SomethingElse())
264 | .Then(v => AnotherThing())
265 | .Done(); // <--- Terminate the pipeline and propagate unhandled exceptions.
266 | ```
267 |
268 | To use the `Done` you must apply the following rule: When you get to the end of a chain of promises, you should either return the last promise or end the chain by calling `Done`.
269 |
270 | To hook into the unhandled exception stream:
271 | ```cs
272 | Promise.UnhandledException += Promise_UnhandledException;
273 | ```
274 |
275 | Then forward the exceptions to your own logging system:
276 | ```cs
277 | private void Promise_UnhandledException(object sender, ExceptionEventArgs e)
278 | {
279 | Log.Error(e.Exception, "An unhandled promises exception occured!");
280 | }
281 | ```
282 |
283 | ## Progress reporting
284 |
285 | Promises can additionally report their progress towards completion, allowing the implementor to give the user feedback on the asynchronous operation. The general convention is to report progress as a value from 0 to 1.
286 |
287 | For this, you can either call `Progress` in the promise definition chain or add a third parameter to the `Then` method.
288 |
289 | Listening for progress reporting from a promise using `Progress`:
290 | ```cs
291 | var promise = new Promise();
292 | promise.Progress((progress) => Log.Info("Current progress is " + (100f * progress) + "%"));
293 | ```
294 |
295 | Listening for progress on a `Then` call:
296 | ```cs
297 | var promiseA = new Promise();
298 | var promiseB = new Promise();
299 | promise
300 | .Then(() => promiseB, null, (progress) => Log.Info("promiseA made progress: " + progress))
301 | .Progress(progress => Log.Info("promiseB made progress: " + progress));
302 | ```
303 |
304 | In order to report progress for a promise, you need to call the `ReportProgress` method:
305 | ```cs
306 | var promise = new Promise();
307 | promise.ReportProgress(0.5f); // Report a 50% completion
308 | ```
309 |
310 | ## Promises that are already Resolved/Rejected
311 |
312 | For convenience or testing you will at some point need to create a promise that *starts out* in the resolved or rejected state. This is easy to achieve using *Resolved* and *Rejected* functions:
313 | ```cs
314 | var resolvedPromise = Promise.Resolved("some result");
315 |
316 | var rejectedPromise = Promise.Rejected(someException);
317 | ```
318 |
319 | ## Interfaces ##
320 |
321 | The class *Promise* implements the following interfaces:
322 |
323 | - `IPromise` Interface to await promise resolution.
324 | - `IPendingPromise` Interface that can resolve or reject the promise.
325 |
326 | ## Combining Multiple Async Operations ##
327 |
328 | The *All* function combines multiple async operations to run in parallel. It converts a collection of promises or a variable length parameter list of promises into a single promise that yields a collection.
329 |
330 | Say that each promise yields a value of type *T*, the resulting promise then yields a collection with values of type *T*.
331 |
332 | Here is an example that extracts links from multiple pages and merges the results:
333 | ```cs
334 | var urls = new List();
335 | urls.Add("www.google.com");
336 | urls.Add("www.yahoo.com");
337 |
338 | Promise
339 | .All(urls.Select(url => Download(url))) // Download each URL.
340 | .Then(pages => // Receives collection of downloaded pages.
341 | pages.SelectMany(
342 | page => ExtractAllLinks(page) // Extract links from all pages then flatten to single collection of links.
343 | )
344 | )
345 | .Done(links => // Receives the flattened collection of links from all pages at once.
346 | {
347 | foreach (var link in links)
348 | {
349 | Console.WriteLine(link);
350 | }
351 | });
352 | ```
353 |
354 | When listening for progress events in an All operation, the progress that you will receive will be the average of all the progress values reported by all the given promises.
355 |
356 | ## Chaining Multiple Async Operations
357 |
358 | The *ThenAll* function is a convenient way of chaining multiple promise onto an existing promise:
359 | ```cs
360 | promise
361 | .Then(result => SomeAsyncOperation(result)) // Chain a single async operation
362 | .ThenAll(result => // Chain multiple async operations.
363 | new IPromise[] // Return an enumerable of promises.
364 | {
365 | SomeAsyncOperation1(result),
366 | SomeAsyncOperation2(result),
367 | SomeAsyncOperation3(result)
368 | }
369 | )
370 | .Done(collection => ...); // Final promise resolves
371 | // with a collection of values
372 | // when all operations have completed.
373 | ```
374 |
375 | ## Racing Asynchronous Operations
376 |
377 | The *Race* and *ThenRace* functions are similar to the *All* and *ThenAll* functions, but it is the first async operation that completes that wins the race and it's value resolves the promise.
378 | ```cs
379 | promise
380 | .Then(result => SomeAsyncOperation(result)) // Chain an async operation.
381 | .ThenRace(result => // Race multiple async operations.
382 | new IPromise[] // Return an enumerable of promises.
383 | {
384 | SomeAsyncOperation1(result),
385 | SomeAsyncOperation2(result),
386 | SomeAsyncOperation3(result)
387 | }
388 | )
389 | .Done(result => ...); // The result has come from whichever of
390 | // the async operations completed first.
391 | ```
392 |
393 | When listening for progress events in a race operation, the progress that you will receive will be the maximum of those reported by all the given promises.
394 |
395 | ## Chaining Synchronous Actions that have no Result
396 |
397 | The *Then* function can be used to chain synchronous operations that yield no result.
398 | ```cs
399 | var promise = ...
400 | promise
401 | .Then(result => SomeAsyncOperation(result)) // Chain an async operation.
402 | .Then(result => Console.WriteLine(result)) // Chain a sync operation that yields no result.
403 | .Done(() => ...); // No result is passed because the previous operation returned nothing.
404 | ```
405 |
406 | ## Promises that have no Results (a non-value promise)
407 |
408 | What about a promise that has no result? This represents an asynchronous operation that promises only to complete, it doesn't promise to yield any value as a result. I call this a non-value promise, as opposed to a value promise, which is a promise that does yield a value. This might seem like a curiousity but it is actually very useful for sequencing visual effects.
409 |
410 | `Promise` is very similar to `Promise` and implements the similar interfaces: `IPromise` and `IPendingPromise`.
411 |
412 | `Promise` functions that affect the resulting value have no relevance for the non-value promise and have been removed.
413 |
414 | As an example consider the chaining of animation and sound effects as we often need to do in *game development*:
415 | ```cs
416 | RunAnimation("Foo") // RunAnimation returns a promise that
417 | .Then(() => RunAnimation("Bar")) // is resolved when the animation is complete.
418 | .Then(() => PlaySound("AnimComplete"));
419 | ```
420 |
421 | ## Convert a value promise to a non-value promise
422 |
423 | From time to time you might want to convert a value promise to a non-value promise or vice versa. Both `Promise` and `Promise` have overloads of `Then` and `ThenAll` that do this conversion. You just need to return the appropriate type of promise (for `Then`) or enumerable of promises (for `ThenAll`).
424 |
425 | As an example consider a recursive link extractor and file downloader function:
426 | ```cs
427 | public IPromise DownloadAll(string url)
428 | {
429 | return DownloadURL(url) // Yields a value, the HTML text downloaded.
430 | .Then(html => ExtractLinks(html)) // Convert HTML into an enumerable of links.
431 | .ThenAll(links => // Process each link.
432 | {
433 | // Determine links that should be followed, then follow them.
434 | var linksToFollow = links.Where(link => IsLinkToFollow(link));
435 | var linksFollowing = linksToFollow.Select(link => DownloadAll(link));
436 |
437 | // Determine links that are files to be downloaded, then download them.
438 | var linksToDownload = links.Where(link => IsLinkToDownload(link));
439 | var linksDownloading = linksToDownload.Select(link => DownloadFile(link));
440 |
441 | // Return an enumerable of promises.
442 | // This combines the recursive link following and any files we want to download.
443 | // Because we are returning an enumerable of non-value promises, the resulting
444 | // chained promises is also non-value.
445 | return linksToFollow.Concat(linksDownloading);
446 | });
447 | }
448 | ```
449 |
450 | Usage:
451 | ```cs
452 | DownloadAll("www.somewhere.com")
453 | .Done(() =>
454 | Console.WriteLine("Recursive download completed.");
455 | );
456 | ```
457 |
458 | ## Running a Sequence of Operations
459 |
460 | The `Sequence` and `ThenSequence` functions build a single promise that wraps multiple sequential operations that will be invoked one after the other.
461 |
462 | Multiple promise-yielding functions are provided as input, these are chained one after the other and wrapped in a single promise that is resolved once the sequence has completed.
463 | ```cs
464 | var sequence = Promise.Sequence(
465 | () => RunAnimation("Foo"),
466 | () => RunAnimation("Bar"),
467 | () => PlaySound("AnimComplete")
468 | );
469 | ```
470 |
471 | The inputs can also be passed as a collection:
472 | ```cs
473 | var operations = ...
474 | var sequence = Promise.Sequence(operations);
475 | ```
476 |
477 | This might be used, for example, to play a variable length collection of animations based on data:
478 | ```cs
479 | var animationNames = ... variable length array of animation names loaded from data...
480 | var animations = animationNames.Select(animName => (Func)(() => RunAnimation(animName)));
481 | var sequence = Promise.Sequence(animations);
482 | sequence
483 | .Done(() =>
484 | {
485 | // All animations have completed in sequence.
486 | });
487 | ```
488 |
489 | Unfortunately we find that we have reached the limits of what is possible with C# type inference, hence the use of the ugly cast `(Func)`.
490 |
491 | The cast can easily be removed by converting the inner anonymous function to an actual function which I'll call `PrepAnimation`:
492 | ```cs
493 | private Func PrepAnimation(string animName)
494 | {
495 | return () => RunAnimation(animName);
496 | }
497 |
498 | var animations = animationNames.Select(animName => PrepAnimation(animName));
499 | var sequence = Promise.Sequence(animations);
500 | sequence
501 | .Done(() =>
502 | {
503 | // All animations have completed in sequence.
504 | });
505 | ```
506 |
507 | Holy cow... we've just careened into [functional programming](http://en.wikipedia.org/wiki/Functional_programming) territory, herein lies very powerful and expressive programming techniques.
508 |
509 | ## Combining Parallel and Sequential Operations
510 |
511 | We can easily combine sequential and parallel operations to build very expressive logic.
512 | ```cs
513 | Promise.Sequence( // Play operations 1 and 2 sequently.
514 | () => Promise.All( // Operation 1: Play animation and sound at same time.
515 | RunAnimation("Foo"),
516 | PlaySound("Bar")
517 | ),
518 | () => Promise.All(
519 | RunAnimation("One"), // Operation 2: Play animation and sound at same time.
520 | PlaySound("Two")
521 | )
522 | );
523 | ```
524 |
525 | I'm starting to feel like we are defining behavior trees.
526 |
527 | ## Weighted averaging of progress on multiple promises
528 |
529 | If you have a promise that comprises a sequence of other promises, you might want to report the total progress for these, and even give more weight to the progress of some promise over another. In this example, we are first downloading an asset from some URL and then we are loading the downloaded asset into memory. We consider that the time it takes to download the asset will be an 80% of the total time, while the time to load it into memory is a 20%:
530 | ```cs
531 | var promise = new Promise();
532 |
533 | Download(url)
534 | .Progress((downloadProgress) => promise.ReportProgress(0.8f * downloadProgress))
535 | .Then((asset) => LoadAssetIntoMemory(asset))
536 | .Progress((loadProgress) => promise.ReportProgress(0.8f + 0.2f * loadProgress))
537 | .Then(() => promise.Resolve())
538 | .Catch((ex) => promise.Reject(ex));
539 |
540 | return promise;
541 | ```
542 |
543 | ## PromiseTimer class
544 |
545 | The promise timer is not part of the Promises/A+ standard but is a utility that makes it possible to create promises that check if a condition is met each time the promise timer is updated. A common usage of this is in games where the promise timer is updated each frame.
546 |
547 | To use it, create an instance of the promise timer and call its `Update` method in your main loop:
548 | ```cs
549 | class Example
550 | {
551 | private IPromiseTimer promiseTimer;
552 |
553 | Example()
554 | {
555 | promiseTimer = new PromiseTimer();
556 | }
557 |
558 | // Run once for every frame - equivilant to Update() in Unity
559 | void MainLoop()
560 | {
561 | // deltaTime is equal to the time since the last MainLoop
562 | promiseTimer.Update(Time.deltaTime);
563 |
564 | // Process your other logic here
565 | }
566 | }
567 | ```
568 |
569 | Note that usually it is best to call `PromiseTimer.Update` *before* your other logic, otherwise you may have unintended behaviour such as promises that are supposed to take a very short time resolving in the same update loop as they were created in.
570 |
571 | ### PromiseTimer.WaitFor
572 |
573 | This method creates a promise that resolves after the specified amount of time in seconds has passed. Time is calculated as the sum of the delta values passed into `PromiseTimer.Update`
574 | ```cs
575 | IPromise LogAfterFourSeconds()
576 | {
577 | return promiseTimer.WaitFor(4f)
578 | .Then(() => Console.Log("4 seconds have passed!"));
579 | }
580 | ```
581 |
582 | ### PromiseTimer.WaitUntil
583 |
584 | WaitUntil takes a predicate to check each update and resolves once the predicate returns true. This predicate function is passed a `TimeData` object, which just contains the most recent frame's `deltaTime` and `elapsedTime` which is the total amount of time since the promise was created.
585 | ```cs
586 | IPromise FadeOut(float duration)
587 | {
588 | return promiseTimer.WaitUntil(timeData =>
589 | {
590 | // Here we are using the amount of elapsed time to calculate what the current
591 | // fade value should be (between 0 and 1).
592 | // Since we're fading our we should be going from 0 (not faded) to 1 (full)
593 | var fadeAmount = Mathf.Clamp01(timeData.elapsedTime / duration);
594 | SetFadeValue(fadeAmount);
595 |
596 | // Resolve the promsie once we've finished.
597 | return fadeAmount >= 1f;
598 | });
599 | }
600 | ```
601 |
602 | ### PromiseTimer.WaitWhile
603 |
604 | WaitWhile is exactly the same as WaitUntil except that it resolves when its predicate function returns false. Think of WaitUntil as running *until* its predicate returns true, and WaitWhile as running *while* its predicate returns true, stopping when it is false.
605 |
606 | ### TimeData struct
607 |
608 | TimeData is passed to you as a paramter when using either PromiseTimer.WaitUntil or PromiseTimer.WaitWhile. It contains the following public fields:
609 |
610 | - elapsedTime
611 | - The amount of time that has elapsed since the pending promise started running
612 | - deltaTime
613 | - The amount of time since the last time the pending promise was updated.
614 | - elapsedUpdates
615 | - The amount of times that PromiseTimer.Update() has been called since the pending promise started running
616 |
617 | ## Examples
618 |
619 |
620 | - Example1
621 | - Example of downloading text from a URL using a promise.
622 | - Example2
623 | - Example of a promise that is rejected because of an error during
624 | - the async operation.
625 | - Example3
626 | - This example downloads search results from google then transforms the result to extract links.
627 | - Includes both error handling and a completion handler.
628 | - Example4
629 | - This example downloads search results from google, extracts the links and follows only a single first link, downloads its then prints the result.
630 | - Includes both error handling and a completion handler.
631 | - Example5
632 | - This example downloads search results from google, extracts the links, follows all (absolute) links and combines all async operations in a single operation using the `All` function.
633 |
--------------------------------------------------------------------------------
/Tests/A+ Spec/2.1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using RSG.Exceptions;
3 | using Xunit;
4 |
5 | namespace RSG.Tests.A__Spec
6 | {
7 | public class _2_1
8 | {
9 | // 2.1.1.1.
10 | [Fact]
11 | public void When_pending_a_promise_may_transition_to_either_the_fulfilled_or_rejected_state()
12 | {
13 | var pendingPromise1 = new Promise