├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── .travis.yml ├── Future.swift.podspec ├── Future.xcodeproj ├── FutureTests_Info.plist ├── Future_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── Future-Package.xcscheme ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── Future │ ├── Async.swift │ ├── Features │ ├── always.swift │ ├── and.swift │ ├── any.swift │ ├── asAny.swift │ ├── asVoid.swift │ ├── catch.swift │ ├── delay.swift │ ├── done.swift │ ├── finally.swift │ ├── flat.swift │ ├── flatMap.swift │ ├── hush.swift │ ├── map.swift │ ├── mute.swift │ ├── pipe.swift │ ├── race.swift │ ├── recover.swift │ ├── reduce.swift │ ├── retry.swift │ ├── return.swift │ ├── some.swift │ ├── tap.swift │ ├── then.swift │ ├── timeout.swift │ ├── validate.swift │ ├── wait.swift │ ├── whenAll.T.gyb │ ├── whenAll.T.swift │ ├── whenAll.swift │ ├── whenAllVoid.T.gyb │ ├── whenAllVoid.T.swift │ ├── whenAllVoid.swift │ ├── whenAny.T.gyb │ ├── whenAny.T.swift │ ├── whenAny.swift │ └── yield.swift │ ├── Future.swift │ ├── Helpers │ ├── Atom.swift │ ├── Extensions.swift │ └── Lock.swift │ ├── Promise.swift │ ├── Publisher.swift │ ├── Scheduler.swift │ └── Thenable.swift ├── Tests ├── FutureTests │ ├── AsyncTests.swift │ ├── FeaturesTests.swift │ ├── FutureTests.swift │ ├── Helpers.swift │ └── XCTestManifests.swift └── LinuxMain.swift ├── benchmark ├── Package.resolved ├── Package.swift ├── README.md ├── Sources │ └── benchmark │ │ ├── main.swift │ │ └── src │ │ ├── BrightFutures.swift │ │ ├── Dispatch.swift │ │ ├── Future.swift │ │ ├── Helpers.swift │ │ ├── Hydra.swift │ │ ├── PromiseKit.swift │ │ ├── Promises.swift │ │ ├── Reactive.swift │ │ └── RxSwift.swift ├── Tests │ ├── LinuxMain.swift │ └── benchmarkTests │ │ ├── XCTestManifests.swift │ │ └── benchmarkTests.swift └── benchmark.xcodeproj │ ├── BrightFutures_Info.plist │ ├── FBLPromises_Info.plist │ ├── FutureQ_Info.plist │ ├── Future_Info.plist │ ├── Hydra_Info.plist │ ├── PromiseKit_Info.plist │ ├── Promises_Info.plist │ ├── ReactiveSwift_Info.plist │ ├── Result_Info.plist │ ├── RxSwift_Info.plist │ ├── benchmarkTests_Info.plist │ ├── project.pbxproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ └── xcschemes │ ├── benchmark-Package.xcscheme │ └── benchmark.xcscheme ├── logo.png ├── resources ├── benchmark-concurrent.png └── benchmark-serial.png └── utils └── gyb.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | 5 | language: objective-c 6 | osx_image: xcode10.2 7 | 8 | env: 9 | global: 10 | - PROJECT="Future.swift.xcodeproj" 11 | - SCHEME="Future.swift-Package" 12 | 13 | matrix: 14 | include: 15 | - os: osx 16 | env: 17 | - SDK="iphonesimulator12.2" 18 | - DESTINATION="platform=iOS Simulator,name=iPhone 8,OS=12.2" 19 | - os: osx 20 | env: 21 | - SDK="macosx10.14" 22 | - DESTINATION="arch=x86_64" 23 | - os: osx 24 | env: 25 | - SDK="appletvsimulator12.0" 26 | - DESTINATION="OS=12.0,name=Apple TV 4K" 27 | - os: linux 28 | sudo: required 29 | dist: trusty 30 | 31 | before_install: 32 | - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then 33 | gem install xcpretty; 34 | fi 35 | - if [[ $TRAVIS_OS_NAME == 'linux' ]]; then 36 | eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"; 37 | fi 38 | 39 | script: 40 | - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then 41 | xcodebuild clean build test -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -enableCodeCoverage YES | xcpretty; 42 | fi 43 | - if [[ $TRAVIS_OS_NAME == 'linux' ]]; then 44 | swift test; 45 | fi 46 | 47 | after_success: 48 | if [[ $TRAVIS_OS_NAME == 'osx' ]]; then 49 | bash <(curl -s https://codecov.io/bash) -J 'Future.swift'; 50 | fi -------------------------------------------------------------------------------- /Future.swift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Future.swift" 3 | s.module_name = "Future" 4 | s.version = "0.0.1" 5 | s.license = { :type => "MIT" } 6 | s.homepage = "https://github.com/luoxiu/Future" 7 | s.author = { "Quentin Jin" => "luoxiustm@gmail.com" } 8 | s.summary = "Future is an implementation of futures and promises for Swift." 9 | 10 | s.source = { :git => "https://github.com/luoxiu/Future.git", :tag => "#{s.version}" } 11 | s.source_files = "Sources/Future/**/*.swift" 12 | 13 | s.swift_version = "5.0" 14 | 15 | s.ios.deployment_target = "10.0" 16 | s.osx.deployment_target = "10.12" 17 | s.tvos.deployment_target = "10.0" 18 | s.watchos.deployment_target = "3.0" 19 | end 20 | -------------------------------------------------------------------------------- /Future.xcodeproj/FutureTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Future.xcodeproj/Future_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Future.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = "1"; 4 | objectVersion = "46"; 5 | objects = { 6 | "Future::Future" = { 7 | isa = "PBXNativeTarget"; 8 | buildConfigurationList = "OBJ_71"; 9 | buildPhases = ( 10 | "OBJ_74", 11 | "OBJ_117" 12 | ); 13 | dependencies = ( 14 | ); 15 | name = "Future"; 16 | productName = "Future"; 17 | productReference = "Future::Future::Product"; 18 | productType = "com.apple.product-type.framework"; 19 | }; 20 | "Future::Future::Product" = { 21 | isa = "PBXFileReference"; 22 | path = "Future.framework"; 23 | sourceTree = "BUILT_PRODUCTS_DIR"; 24 | }; 25 | "Future::FuturePackageTests::ProductTarget" = { 26 | isa = "PBXAggregateTarget"; 27 | buildConfigurationList = "OBJ_125"; 28 | buildPhases = ( 29 | ); 30 | dependencies = ( 31 | "OBJ_128" 32 | ); 33 | name = "FuturePackageTests"; 34 | productName = "FuturePackageTests"; 35 | }; 36 | "Future::FutureTests" = { 37 | isa = "PBXNativeTarget"; 38 | buildConfigurationList = "OBJ_130"; 39 | buildPhases = ( 40 | "OBJ_133", 41 | "OBJ_139" 42 | ); 43 | dependencies = ( 44 | "OBJ_141" 45 | ); 46 | name = "FutureTests"; 47 | productName = "FutureTests"; 48 | productReference = "Future::FutureTests::Product"; 49 | productType = "com.apple.product-type.bundle.unit-test"; 50 | }; 51 | "Future::FutureTests::Product" = { 52 | isa = "PBXFileReference"; 53 | path = "FutureTests.xctest"; 54 | sourceTree = "BUILT_PRODUCTS_DIR"; 55 | }; 56 | "Future::SwiftPMPackageDescription" = { 57 | isa = "PBXNativeTarget"; 58 | buildConfigurationList = "OBJ_119"; 59 | buildPhases = ( 60 | "OBJ_122" 61 | ); 62 | dependencies = ( 63 | ); 64 | name = "FuturePackageDescription"; 65 | productName = "FuturePackageDescription"; 66 | productType = "com.apple.product-type.framework"; 67 | }; 68 | "OBJ_1" = { 69 | isa = "PBXProject"; 70 | attributes = { 71 | LastSwiftMigration = "9999"; 72 | LastUpgradeCheck = "9999"; 73 | }; 74 | buildConfigurationList = "OBJ_2"; 75 | compatibilityVersion = "Xcode 3.2"; 76 | developmentRegion = "English"; 77 | hasScannedForEncodings = "0"; 78 | knownRegions = ( 79 | "en" 80 | ); 81 | mainGroup = "OBJ_5"; 82 | productRefGroup = "OBJ_60"; 83 | projectDirPath = "."; 84 | targets = ( 85 | "Future::Future", 86 | "Future::SwiftPMPackageDescription", 87 | "Future::FuturePackageTests::ProductTarget", 88 | "Future::FutureTests" 89 | ); 90 | }; 91 | "OBJ_10" = { 92 | isa = "PBXGroup"; 93 | children = ( 94 | "OBJ_11", 95 | "OBJ_12", 96 | "OBJ_13", 97 | "OBJ_14", 98 | "OBJ_15", 99 | "OBJ_16", 100 | "OBJ_17", 101 | "OBJ_18", 102 | "OBJ_19", 103 | "OBJ_20", 104 | "OBJ_21", 105 | "OBJ_22", 106 | "OBJ_23", 107 | "OBJ_24", 108 | "OBJ_25", 109 | "OBJ_26", 110 | "OBJ_27", 111 | "OBJ_28", 112 | "OBJ_29", 113 | "OBJ_30", 114 | "OBJ_31", 115 | "OBJ_32", 116 | "OBJ_33", 117 | "OBJ_34", 118 | "OBJ_35", 119 | "OBJ_36", 120 | "OBJ_37", 121 | "OBJ_38", 122 | "OBJ_39", 123 | "OBJ_40", 124 | "OBJ_41", 125 | "OBJ_42", 126 | "OBJ_43" 127 | ); 128 | name = "Features"; 129 | path = "Features"; 130 | sourceTree = ""; 131 | }; 132 | "OBJ_100" = { 133 | isa = "PBXBuildFile"; 134 | fileRef = "OBJ_35"; 135 | }; 136 | "OBJ_101" = { 137 | isa = "PBXBuildFile"; 138 | fileRef = "OBJ_36"; 139 | }; 140 | "OBJ_102" = { 141 | isa = "PBXBuildFile"; 142 | fileRef = "OBJ_37"; 143 | }; 144 | "OBJ_103" = { 145 | isa = "PBXBuildFile"; 146 | fileRef = "OBJ_38"; 147 | }; 148 | "OBJ_104" = { 149 | isa = "PBXBuildFile"; 150 | fileRef = "OBJ_39"; 151 | }; 152 | "OBJ_105" = { 153 | isa = "PBXBuildFile"; 154 | fileRef = "OBJ_40"; 155 | }; 156 | "OBJ_106" = { 157 | isa = "PBXBuildFile"; 158 | fileRef = "OBJ_41"; 159 | }; 160 | "OBJ_107" = { 161 | isa = "PBXBuildFile"; 162 | fileRef = "OBJ_42"; 163 | }; 164 | "OBJ_108" = { 165 | isa = "PBXBuildFile"; 166 | fileRef = "OBJ_43"; 167 | }; 168 | "OBJ_109" = { 169 | isa = "PBXBuildFile"; 170 | fileRef = "OBJ_44"; 171 | }; 172 | "OBJ_11" = { 173 | isa = "PBXFileReference"; 174 | path = "always.swift"; 175 | sourceTree = ""; 176 | }; 177 | "OBJ_110" = { 178 | isa = "PBXBuildFile"; 179 | fileRef = "OBJ_46"; 180 | }; 181 | "OBJ_111" = { 182 | isa = "PBXBuildFile"; 183 | fileRef = "OBJ_47"; 184 | }; 185 | "OBJ_112" = { 186 | isa = "PBXBuildFile"; 187 | fileRef = "OBJ_48"; 188 | }; 189 | "OBJ_113" = { 190 | isa = "PBXBuildFile"; 191 | fileRef = "OBJ_49"; 192 | }; 193 | "OBJ_114" = { 194 | isa = "PBXBuildFile"; 195 | fileRef = "OBJ_50"; 196 | }; 197 | "OBJ_115" = { 198 | isa = "PBXBuildFile"; 199 | fileRef = "OBJ_51"; 200 | }; 201 | "OBJ_116" = { 202 | isa = "PBXBuildFile"; 203 | fileRef = "OBJ_52"; 204 | }; 205 | "OBJ_117" = { 206 | isa = "PBXFrameworksBuildPhase"; 207 | files = ( 208 | ); 209 | }; 210 | "OBJ_119" = { 211 | isa = "XCConfigurationList"; 212 | buildConfigurations = ( 213 | "OBJ_120", 214 | "OBJ_121" 215 | ); 216 | defaultConfigurationIsVisible = "0"; 217 | defaultConfigurationName = "Release"; 218 | }; 219 | "OBJ_12" = { 220 | isa = "PBXFileReference"; 221 | path = "and.swift"; 222 | sourceTree = ""; 223 | }; 224 | "OBJ_120" = { 225 | isa = "XCBuildConfiguration"; 226 | buildSettings = { 227 | LD = "/usr/bin/true"; 228 | OTHER_SWIFT_FLAGS = ( 229 | "-swift-version", 230 | "5", 231 | "-I", 232 | "$(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2", 233 | "-target", 234 | "x86_64-apple-macosx10.10", 235 | "-sdk", 236 | "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk" 237 | ); 238 | SWIFT_VERSION = "5.0"; 239 | }; 240 | name = "Debug"; 241 | }; 242 | "OBJ_121" = { 243 | isa = "XCBuildConfiguration"; 244 | buildSettings = { 245 | LD = "/usr/bin/true"; 246 | OTHER_SWIFT_FLAGS = ( 247 | "-swift-version", 248 | "5", 249 | "-I", 250 | "$(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2", 251 | "-target", 252 | "x86_64-apple-macosx10.10", 253 | "-sdk", 254 | "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk" 255 | ); 256 | SWIFT_VERSION = "5.0"; 257 | }; 258 | name = "Release"; 259 | }; 260 | "OBJ_122" = { 261 | isa = "PBXSourcesBuildPhase"; 262 | files = ( 263 | "OBJ_123" 264 | ); 265 | }; 266 | "OBJ_123" = { 267 | isa = "PBXBuildFile"; 268 | fileRef = "OBJ_6"; 269 | }; 270 | "OBJ_125" = { 271 | isa = "XCConfigurationList"; 272 | buildConfigurations = ( 273 | "OBJ_126", 274 | "OBJ_127" 275 | ); 276 | defaultConfigurationIsVisible = "0"; 277 | defaultConfigurationName = "Release"; 278 | }; 279 | "OBJ_126" = { 280 | isa = "XCBuildConfiguration"; 281 | buildSettings = { 282 | }; 283 | name = "Debug"; 284 | }; 285 | "OBJ_127" = { 286 | isa = "XCBuildConfiguration"; 287 | buildSettings = { 288 | }; 289 | name = "Release"; 290 | }; 291 | "OBJ_128" = { 292 | isa = "PBXTargetDependency"; 293 | target = "Future::FutureTests"; 294 | }; 295 | "OBJ_13" = { 296 | isa = "PBXFileReference"; 297 | path = "any.swift"; 298 | sourceTree = ""; 299 | }; 300 | "OBJ_130" = { 301 | isa = "XCConfigurationList"; 302 | buildConfigurations = ( 303 | "OBJ_131", 304 | "OBJ_132" 305 | ); 306 | defaultConfigurationIsVisible = "0"; 307 | defaultConfigurationName = "Release"; 308 | }; 309 | "OBJ_131" = { 310 | isa = "XCBuildConfiguration"; 311 | buildSettings = { 312 | CLANG_ENABLE_MODULES = "YES"; 313 | EMBEDDED_CONTENT_CONTAINS_SWIFT = "YES"; 314 | FRAMEWORK_SEARCH_PATHS = ( 315 | "$(inherited)", 316 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 317 | ); 318 | HEADER_SEARCH_PATHS = ( 319 | "$(inherited)" 320 | ); 321 | INFOPLIST_FILE = "Future.xcodeproj/FutureTests_Info.plist"; 322 | IPHONEOS_DEPLOYMENT_TARGET = "10.0"; 323 | LD_RUNPATH_SEARCH_PATHS = ( 324 | "$(inherited)", 325 | "@loader_path/../Frameworks", 326 | "@loader_path/Frameworks" 327 | ); 328 | MACOSX_DEPLOYMENT_TARGET = "10.12"; 329 | OTHER_CFLAGS = ( 330 | "$(inherited)" 331 | ); 332 | OTHER_LDFLAGS = ( 333 | "$(inherited)" 334 | ); 335 | OTHER_SWIFT_FLAGS = ( 336 | "$(inherited)" 337 | ); 338 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 339 | "$(inherited)" 340 | ); 341 | SWIFT_VERSION = "5.0"; 342 | TARGET_NAME = "FutureTests"; 343 | TVOS_DEPLOYMENT_TARGET = "10.0"; 344 | WATCHOS_DEPLOYMENT_TARGET = "3.0"; 345 | }; 346 | name = "Debug"; 347 | }; 348 | "OBJ_132" = { 349 | isa = "XCBuildConfiguration"; 350 | buildSettings = { 351 | CLANG_ENABLE_MODULES = "YES"; 352 | EMBEDDED_CONTENT_CONTAINS_SWIFT = "YES"; 353 | FRAMEWORK_SEARCH_PATHS = ( 354 | "$(inherited)", 355 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 356 | ); 357 | HEADER_SEARCH_PATHS = ( 358 | "$(inherited)" 359 | ); 360 | INFOPLIST_FILE = "Future.xcodeproj/FutureTests_Info.plist"; 361 | IPHONEOS_DEPLOYMENT_TARGET = "10.0"; 362 | LD_RUNPATH_SEARCH_PATHS = ( 363 | "$(inherited)", 364 | "@loader_path/../Frameworks", 365 | "@loader_path/Frameworks" 366 | ); 367 | MACOSX_DEPLOYMENT_TARGET = "10.12"; 368 | OTHER_CFLAGS = ( 369 | "$(inherited)" 370 | ); 371 | OTHER_LDFLAGS = ( 372 | "$(inherited)" 373 | ); 374 | OTHER_SWIFT_FLAGS = ( 375 | "$(inherited)" 376 | ); 377 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 378 | "$(inherited)" 379 | ); 380 | SWIFT_VERSION = "5.0"; 381 | TARGET_NAME = "FutureTests"; 382 | TVOS_DEPLOYMENT_TARGET = "10.0"; 383 | WATCHOS_DEPLOYMENT_TARGET = "3.0"; 384 | }; 385 | name = "Release"; 386 | }; 387 | "OBJ_133" = { 388 | isa = "PBXSourcesBuildPhase"; 389 | files = ( 390 | "OBJ_134", 391 | "OBJ_135", 392 | "OBJ_136", 393 | "OBJ_137", 394 | "OBJ_138" 395 | ); 396 | }; 397 | "OBJ_134" = { 398 | isa = "PBXBuildFile"; 399 | fileRef = "OBJ_55"; 400 | }; 401 | "OBJ_135" = { 402 | isa = "PBXBuildFile"; 403 | fileRef = "OBJ_56"; 404 | }; 405 | "OBJ_136" = { 406 | isa = "PBXBuildFile"; 407 | fileRef = "OBJ_57"; 408 | }; 409 | "OBJ_137" = { 410 | isa = "PBXBuildFile"; 411 | fileRef = "OBJ_58"; 412 | }; 413 | "OBJ_138" = { 414 | isa = "PBXBuildFile"; 415 | fileRef = "OBJ_59"; 416 | }; 417 | "OBJ_139" = { 418 | isa = "PBXFrameworksBuildPhase"; 419 | files = ( 420 | "OBJ_140" 421 | ); 422 | }; 423 | "OBJ_14" = { 424 | isa = "PBXFileReference"; 425 | path = "asAny.swift"; 426 | sourceTree = ""; 427 | }; 428 | "OBJ_140" = { 429 | isa = "PBXBuildFile"; 430 | fileRef = "Future::Future::Product"; 431 | }; 432 | "OBJ_141" = { 433 | isa = "PBXTargetDependency"; 434 | target = "Future::Future"; 435 | }; 436 | "OBJ_15" = { 437 | isa = "PBXFileReference"; 438 | path = "asVoid.swift"; 439 | sourceTree = ""; 440 | }; 441 | "OBJ_16" = { 442 | isa = "PBXFileReference"; 443 | path = "catch.swift"; 444 | sourceTree = ""; 445 | }; 446 | "OBJ_17" = { 447 | isa = "PBXFileReference"; 448 | path = "delay.swift"; 449 | sourceTree = ""; 450 | }; 451 | "OBJ_18" = { 452 | isa = "PBXFileReference"; 453 | path = "done.swift"; 454 | sourceTree = ""; 455 | }; 456 | "OBJ_19" = { 457 | isa = "PBXFileReference"; 458 | path = "finally.swift"; 459 | sourceTree = ""; 460 | }; 461 | "OBJ_2" = { 462 | isa = "XCConfigurationList"; 463 | buildConfigurations = ( 464 | "OBJ_3", 465 | "OBJ_4" 466 | ); 467 | defaultConfigurationIsVisible = "0"; 468 | defaultConfigurationName = "Release"; 469 | }; 470 | "OBJ_20" = { 471 | isa = "PBXFileReference"; 472 | path = "flat.swift"; 473 | sourceTree = ""; 474 | }; 475 | "OBJ_21" = { 476 | isa = "PBXFileReference"; 477 | path = "flatMap.swift"; 478 | sourceTree = ""; 479 | }; 480 | "OBJ_22" = { 481 | isa = "PBXFileReference"; 482 | path = "hush.swift"; 483 | sourceTree = ""; 484 | }; 485 | "OBJ_23" = { 486 | isa = "PBXFileReference"; 487 | path = "map.swift"; 488 | sourceTree = ""; 489 | }; 490 | "OBJ_24" = { 491 | isa = "PBXFileReference"; 492 | path = "mute.swift"; 493 | sourceTree = ""; 494 | }; 495 | "OBJ_25" = { 496 | isa = "PBXFileReference"; 497 | path = "pipe.swift"; 498 | sourceTree = ""; 499 | }; 500 | "OBJ_26" = { 501 | isa = "PBXFileReference"; 502 | path = "race.swift"; 503 | sourceTree = ""; 504 | }; 505 | "OBJ_27" = { 506 | isa = "PBXFileReference"; 507 | path = "recover.swift"; 508 | sourceTree = ""; 509 | }; 510 | "OBJ_28" = { 511 | isa = "PBXFileReference"; 512 | path = "reduce.swift"; 513 | sourceTree = ""; 514 | }; 515 | "OBJ_29" = { 516 | isa = "PBXFileReference"; 517 | path = "retry.swift"; 518 | sourceTree = ""; 519 | }; 520 | "OBJ_3" = { 521 | isa = "XCBuildConfiguration"; 522 | buildSettings = { 523 | CLANG_ENABLE_OBJC_ARC = "YES"; 524 | COMBINE_HIDPI_IMAGES = "YES"; 525 | COPY_PHASE_STRIP = "NO"; 526 | DEBUG_INFORMATION_FORMAT = "dwarf"; 527 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 528 | ENABLE_NS_ASSERTIONS = "YES"; 529 | GCC_OPTIMIZATION_LEVEL = "0"; 530 | GCC_PREPROCESSOR_DEFINITIONS = ( 531 | "$(inherited)", 532 | "SWIFT_PACKAGE=1", 533 | "DEBUG=1" 534 | ); 535 | MACOSX_DEPLOYMENT_TARGET = "10.10"; 536 | ONLY_ACTIVE_ARCH = "YES"; 537 | OTHER_SWIFT_FLAGS = ( 538 | "-DXcode" 539 | ); 540 | PRODUCT_NAME = "$(TARGET_NAME)"; 541 | SDKROOT = "macosx"; 542 | SUPPORTED_PLATFORMS = ( 543 | "macosx", 544 | "iphoneos", 545 | "iphonesimulator", 546 | "appletvos", 547 | "appletvsimulator", 548 | "watchos", 549 | "watchsimulator" 550 | ); 551 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 552 | "$(inherited)", 553 | "SWIFT_PACKAGE", 554 | "DEBUG" 555 | ); 556 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 557 | USE_HEADERMAP = "NO"; 558 | }; 559 | name = "Debug"; 560 | }; 561 | "OBJ_30" = { 562 | isa = "PBXFileReference"; 563 | path = "return.swift"; 564 | sourceTree = ""; 565 | }; 566 | "OBJ_31" = { 567 | isa = "PBXFileReference"; 568 | path = "some.swift"; 569 | sourceTree = ""; 570 | }; 571 | "OBJ_32" = { 572 | isa = "PBXFileReference"; 573 | path = "tap.swift"; 574 | sourceTree = ""; 575 | }; 576 | "OBJ_33" = { 577 | isa = "PBXFileReference"; 578 | path = "then.swift"; 579 | sourceTree = ""; 580 | }; 581 | "OBJ_34" = { 582 | isa = "PBXFileReference"; 583 | path = "timeout.swift"; 584 | sourceTree = ""; 585 | }; 586 | "OBJ_35" = { 587 | isa = "PBXFileReference"; 588 | path = "validate.swift"; 589 | sourceTree = ""; 590 | }; 591 | "OBJ_36" = { 592 | isa = "PBXFileReference"; 593 | path = "wait.swift"; 594 | sourceTree = ""; 595 | }; 596 | "OBJ_37" = { 597 | isa = "PBXFileReference"; 598 | path = "whenAll.T.swift"; 599 | sourceTree = ""; 600 | }; 601 | "OBJ_38" = { 602 | isa = "PBXFileReference"; 603 | path = "whenAll.swift"; 604 | sourceTree = ""; 605 | }; 606 | "OBJ_39" = { 607 | isa = "PBXFileReference"; 608 | path = "whenAllVoid.T.swift"; 609 | sourceTree = ""; 610 | }; 611 | "OBJ_4" = { 612 | isa = "XCBuildConfiguration"; 613 | buildSettings = { 614 | CLANG_ENABLE_OBJC_ARC = "YES"; 615 | COMBINE_HIDPI_IMAGES = "YES"; 616 | COPY_PHASE_STRIP = "YES"; 617 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 618 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 619 | GCC_OPTIMIZATION_LEVEL = "s"; 620 | GCC_PREPROCESSOR_DEFINITIONS = ( 621 | "$(inherited)", 622 | "SWIFT_PACKAGE=1" 623 | ); 624 | MACOSX_DEPLOYMENT_TARGET = "10.10"; 625 | OTHER_SWIFT_FLAGS = ( 626 | "-DXcode" 627 | ); 628 | PRODUCT_NAME = "$(TARGET_NAME)"; 629 | SDKROOT = "macosx"; 630 | SUPPORTED_PLATFORMS = ( 631 | "macosx", 632 | "iphoneos", 633 | "iphonesimulator", 634 | "appletvos", 635 | "appletvsimulator", 636 | "watchos", 637 | "watchsimulator" 638 | ); 639 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 640 | "$(inherited)", 641 | "SWIFT_PACKAGE" 642 | ); 643 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 644 | USE_HEADERMAP = "NO"; 645 | }; 646 | name = "Release"; 647 | }; 648 | "OBJ_40" = { 649 | isa = "PBXFileReference"; 650 | path = "whenAllVoid.swift"; 651 | sourceTree = ""; 652 | }; 653 | "OBJ_41" = { 654 | isa = "PBXFileReference"; 655 | path = "whenAny.T.swift"; 656 | sourceTree = ""; 657 | }; 658 | "OBJ_42" = { 659 | isa = "PBXFileReference"; 660 | path = "whenAny.swift"; 661 | sourceTree = ""; 662 | }; 663 | "OBJ_43" = { 664 | isa = "PBXFileReference"; 665 | path = "yield.swift"; 666 | sourceTree = ""; 667 | }; 668 | "OBJ_44" = { 669 | isa = "PBXFileReference"; 670 | path = "Future.swift"; 671 | sourceTree = ""; 672 | }; 673 | "OBJ_45" = { 674 | isa = "PBXGroup"; 675 | children = ( 676 | "OBJ_46", 677 | "OBJ_47", 678 | "OBJ_48" 679 | ); 680 | name = "Helpers"; 681 | path = "Helpers"; 682 | sourceTree = ""; 683 | }; 684 | "OBJ_46" = { 685 | isa = "PBXFileReference"; 686 | path = "Atom.swift"; 687 | sourceTree = ""; 688 | }; 689 | "OBJ_47" = { 690 | isa = "PBXFileReference"; 691 | path = "Extensions.swift"; 692 | sourceTree = ""; 693 | }; 694 | "OBJ_48" = { 695 | isa = "PBXFileReference"; 696 | path = "Lock.swift"; 697 | sourceTree = ""; 698 | }; 699 | "OBJ_49" = { 700 | isa = "PBXFileReference"; 701 | path = "Promise.swift"; 702 | sourceTree = ""; 703 | }; 704 | "OBJ_5" = { 705 | isa = "PBXGroup"; 706 | children = ( 707 | "OBJ_6", 708 | "OBJ_7", 709 | "OBJ_53", 710 | "OBJ_60", 711 | "OBJ_63", 712 | "OBJ_64", 713 | "OBJ_65", 714 | "OBJ_66", 715 | "OBJ_67", 716 | "OBJ_68", 717 | "OBJ_69" 718 | ); 719 | path = ""; 720 | sourceTree = ""; 721 | }; 722 | "OBJ_50" = { 723 | isa = "PBXFileReference"; 724 | path = "Publisher.swift"; 725 | sourceTree = ""; 726 | }; 727 | "OBJ_51" = { 728 | isa = "PBXFileReference"; 729 | path = "Scheduler.swift"; 730 | sourceTree = ""; 731 | }; 732 | "OBJ_52" = { 733 | isa = "PBXFileReference"; 734 | path = "Thenable.swift"; 735 | sourceTree = ""; 736 | }; 737 | "OBJ_53" = { 738 | isa = "PBXGroup"; 739 | children = ( 740 | "OBJ_54" 741 | ); 742 | name = "Tests"; 743 | path = ""; 744 | sourceTree = "SOURCE_ROOT"; 745 | }; 746 | "OBJ_54" = { 747 | isa = "PBXGroup"; 748 | children = ( 749 | "OBJ_55", 750 | "OBJ_56", 751 | "OBJ_57", 752 | "OBJ_58", 753 | "OBJ_59" 754 | ); 755 | name = "FutureTests"; 756 | path = "Tests/FutureTests"; 757 | sourceTree = "SOURCE_ROOT"; 758 | }; 759 | "OBJ_55" = { 760 | isa = "PBXFileReference"; 761 | path = "AsyncTests.swift"; 762 | sourceTree = ""; 763 | }; 764 | "OBJ_56" = { 765 | isa = "PBXFileReference"; 766 | path = "FeaturesTests.swift"; 767 | sourceTree = ""; 768 | }; 769 | "OBJ_57" = { 770 | isa = "PBXFileReference"; 771 | path = "FutureTests.swift"; 772 | sourceTree = ""; 773 | }; 774 | "OBJ_58" = { 775 | isa = "PBXFileReference"; 776 | path = "Helpers.swift"; 777 | sourceTree = ""; 778 | }; 779 | "OBJ_59" = { 780 | isa = "PBXFileReference"; 781 | path = "XCTestManifests.swift"; 782 | sourceTree = ""; 783 | }; 784 | "OBJ_6" = { 785 | isa = "PBXFileReference"; 786 | explicitFileType = "sourcecode.swift"; 787 | path = "Package.swift"; 788 | sourceTree = ""; 789 | }; 790 | "OBJ_60" = { 791 | isa = "PBXGroup"; 792 | children = ( 793 | "Future::Future::Product", 794 | "Future::FutureTests::Product" 795 | ); 796 | name = "Products"; 797 | path = ""; 798 | sourceTree = "BUILT_PRODUCTS_DIR"; 799 | }; 800 | "OBJ_63" = { 801 | isa = "PBXFileReference"; 802 | path = "benchmark"; 803 | sourceTree = "SOURCE_ROOT"; 804 | }; 805 | "OBJ_64" = { 806 | isa = "PBXFileReference"; 807 | path = "resources"; 808 | sourceTree = "SOURCE_ROOT"; 809 | }; 810 | "OBJ_65" = { 811 | isa = "PBXFileReference"; 812 | path = "utils"; 813 | sourceTree = "SOURCE_ROOT"; 814 | }; 815 | "OBJ_66" = { 816 | isa = "PBXFileReference"; 817 | path = "LICENSE"; 818 | sourceTree = ""; 819 | }; 820 | "OBJ_67" = { 821 | isa = "PBXFileReference"; 822 | path = "Future.swift.podspec"; 823 | sourceTree = ""; 824 | }; 825 | "OBJ_68" = { 826 | isa = "PBXFileReference"; 827 | path = "README.md"; 828 | sourceTree = ""; 829 | }; 830 | "OBJ_69" = { 831 | isa = "PBXFileReference"; 832 | path = "logo.png"; 833 | sourceTree = ""; 834 | }; 835 | "OBJ_7" = { 836 | isa = "PBXGroup"; 837 | children = ( 838 | "OBJ_8" 839 | ); 840 | name = "Sources"; 841 | path = ""; 842 | sourceTree = "SOURCE_ROOT"; 843 | }; 844 | "OBJ_71" = { 845 | isa = "XCConfigurationList"; 846 | buildConfigurations = ( 847 | "OBJ_72", 848 | "OBJ_73" 849 | ); 850 | defaultConfigurationIsVisible = "0"; 851 | defaultConfigurationName = "Release"; 852 | }; 853 | "OBJ_72" = { 854 | isa = "XCBuildConfiguration"; 855 | buildSettings = { 856 | ENABLE_TESTABILITY = "YES"; 857 | FRAMEWORK_SEARCH_PATHS = ( 858 | "$(inherited)", 859 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 860 | ); 861 | HEADER_SEARCH_PATHS = ( 862 | "$(inherited)" 863 | ); 864 | INFOPLIST_FILE = "Future.xcodeproj/Future_Info.plist"; 865 | IPHONEOS_DEPLOYMENT_TARGET = "10.0"; 866 | LD_RUNPATH_SEARCH_PATHS = ( 867 | "$(inherited)", 868 | "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx" 869 | ); 870 | MACOSX_DEPLOYMENT_TARGET = "10.12"; 871 | OTHER_CFLAGS = ( 872 | "$(inherited)" 873 | ); 874 | OTHER_LDFLAGS = ( 875 | "$(inherited)" 876 | ); 877 | OTHER_SWIFT_FLAGS = ( 878 | "$(inherited)" 879 | ); 880 | PRODUCT_BUNDLE_IDENTIFIER = "Future"; 881 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 882 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 883 | SKIP_INSTALL = "YES"; 884 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 885 | "$(inherited)" 886 | ); 887 | SWIFT_VERSION = "5.0"; 888 | TARGET_NAME = "Future"; 889 | TVOS_DEPLOYMENT_TARGET = "10.0"; 890 | WATCHOS_DEPLOYMENT_TARGET = "3.0"; 891 | }; 892 | name = "Debug"; 893 | }; 894 | "OBJ_73" = { 895 | isa = "XCBuildConfiguration"; 896 | buildSettings = { 897 | ENABLE_TESTABILITY = "YES"; 898 | FRAMEWORK_SEARCH_PATHS = ( 899 | "$(inherited)", 900 | "$(PLATFORM_DIR)/Developer/Library/Frameworks" 901 | ); 902 | HEADER_SEARCH_PATHS = ( 903 | "$(inherited)" 904 | ); 905 | INFOPLIST_FILE = "Future.xcodeproj/Future_Info.plist"; 906 | IPHONEOS_DEPLOYMENT_TARGET = "10.0"; 907 | LD_RUNPATH_SEARCH_PATHS = ( 908 | "$(inherited)", 909 | "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx" 910 | ); 911 | MACOSX_DEPLOYMENT_TARGET = "10.12"; 912 | OTHER_CFLAGS = ( 913 | "$(inherited)" 914 | ); 915 | OTHER_LDFLAGS = ( 916 | "$(inherited)" 917 | ); 918 | OTHER_SWIFT_FLAGS = ( 919 | "$(inherited)" 920 | ); 921 | PRODUCT_BUNDLE_IDENTIFIER = "Future"; 922 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 923 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 924 | SKIP_INSTALL = "YES"; 925 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( 926 | "$(inherited)" 927 | ); 928 | SWIFT_VERSION = "5.0"; 929 | TARGET_NAME = "Future"; 930 | TVOS_DEPLOYMENT_TARGET = "10.0"; 931 | WATCHOS_DEPLOYMENT_TARGET = "3.0"; 932 | }; 933 | name = "Release"; 934 | }; 935 | "OBJ_74" = { 936 | isa = "PBXSourcesBuildPhase"; 937 | files = ( 938 | "OBJ_75", 939 | "OBJ_76", 940 | "OBJ_77", 941 | "OBJ_78", 942 | "OBJ_79", 943 | "OBJ_80", 944 | "OBJ_81", 945 | "OBJ_82", 946 | "OBJ_83", 947 | "OBJ_84", 948 | "OBJ_85", 949 | "OBJ_86", 950 | "OBJ_87", 951 | "OBJ_88", 952 | "OBJ_89", 953 | "OBJ_90", 954 | "OBJ_91", 955 | "OBJ_92", 956 | "OBJ_93", 957 | "OBJ_94", 958 | "OBJ_95", 959 | "OBJ_96", 960 | "OBJ_97", 961 | "OBJ_98", 962 | "OBJ_99", 963 | "OBJ_100", 964 | "OBJ_101", 965 | "OBJ_102", 966 | "OBJ_103", 967 | "OBJ_104", 968 | "OBJ_105", 969 | "OBJ_106", 970 | "OBJ_107", 971 | "OBJ_108", 972 | "OBJ_109", 973 | "OBJ_110", 974 | "OBJ_111", 975 | "OBJ_112", 976 | "OBJ_113", 977 | "OBJ_114", 978 | "OBJ_115", 979 | "OBJ_116" 980 | ); 981 | }; 982 | "OBJ_75" = { 983 | isa = "PBXBuildFile"; 984 | fileRef = "OBJ_9"; 985 | }; 986 | "OBJ_76" = { 987 | isa = "PBXBuildFile"; 988 | fileRef = "OBJ_11"; 989 | }; 990 | "OBJ_77" = { 991 | isa = "PBXBuildFile"; 992 | fileRef = "OBJ_12"; 993 | }; 994 | "OBJ_78" = { 995 | isa = "PBXBuildFile"; 996 | fileRef = "OBJ_13"; 997 | }; 998 | "OBJ_79" = { 999 | isa = "PBXBuildFile"; 1000 | fileRef = "OBJ_14"; 1001 | }; 1002 | "OBJ_8" = { 1003 | isa = "PBXGroup"; 1004 | children = ( 1005 | "OBJ_9", 1006 | "OBJ_10", 1007 | "OBJ_44", 1008 | "OBJ_45", 1009 | "OBJ_49", 1010 | "OBJ_50", 1011 | "OBJ_51", 1012 | "OBJ_52" 1013 | ); 1014 | name = "Future"; 1015 | path = "Sources/Future"; 1016 | sourceTree = "SOURCE_ROOT"; 1017 | }; 1018 | "OBJ_80" = { 1019 | isa = "PBXBuildFile"; 1020 | fileRef = "OBJ_15"; 1021 | }; 1022 | "OBJ_81" = { 1023 | isa = "PBXBuildFile"; 1024 | fileRef = "OBJ_16"; 1025 | }; 1026 | "OBJ_82" = { 1027 | isa = "PBXBuildFile"; 1028 | fileRef = "OBJ_17"; 1029 | }; 1030 | "OBJ_83" = { 1031 | isa = "PBXBuildFile"; 1032 | fileRef = "OBJ_18"; 1033 | }; 1034 | "OBJ_84" = { 1035 | isa = "PBXBuildFile"; 1036 | fileRef = "OBJ_19"; 1037 | }; 1038 | "OBJ_85" = { 1039 | isa = "PBXBuildFile"; 1040 | fileRef = "OBJ_20"; 1041 | }; 1042 | "OBJ_86" = { 1043 | isa = "PBXBuildFile"; 1044 | fileRef = "OBJ_21"; 1045 | }; 1046 | "OBJ_87" = { 1047 | isa = "PBXBuildFile"; 1048 | fileRef = "OBJ_22"; 1049 | }; 1050 | "OBJ_88" = { 1051 | isa = "PBXBuildFile"; 1052 | fileRef = "OBJ_23"; 1053 | }; 1054 | "OBJ_89" = { 1055 | isa = "PBXBuildFile"; 1056 | fileRef = "OBJ_24"; 1057 | }; 1058 | "OBJ_9" = { 1059 | isa = "PBXFileReference"; 1060 | path = "Async.swift"; 1061 | sourceTree = ""; 1062 | }; 1063 | "OBJ_90" = { 1064 | isa = "PBXBuildFile"; 1065 | fileRef = "OBJ_25"; 1066 | }; 1067 | "OBJ_91" = { 1068 | isa = "PBXBuildFile"; 1069 | fileRef = "OBJ_26"; 1070 | }; 1071 | "OBJ_92" = { 1072 | isa = "PBXBuildFile"; 1073 | fileRef = "OBJ_27"; 1074 | }; 1075 | "OBJ_93" = { 1076 | isa = "PBXBuildFile"; 1077 | fileRef = "OBJ_28"; 1078 | }; 1079 | "OBJ_94" = { 1080 | isa = "PBXBuildFile"; 1081 | fileRef = "OBJ_29"; 1082 | }; 1083 | "OBJ_95" = { 1084 | isa = "PBXBuildFile"; 1085 | fileRef = "OBJ_30"; 1086 | }; 1087 | "OBJ_96" = { 1088 | isa = "PBXBuildFile"; 1089 | fileRef = "OBJ_31"; 1090 | }; 1091 | "OBJ_97" = { 1092 | isa = "PBXBuildFile"; 1093 | fileRef = "OBJ_32"; 1094 | }; 1095 | "OBJ_98" = { 1096 | isa = "PBXBuildFile"; 1097 | fileRef = "OBJ_33"; 1098 | }; 1099 | "OBJ_99" = { 1100 | isa = "PBXBuildFile"; 1101 | fileRef = "OBJ_34"; 1102 | }; 1103 | }; 1104 | rootObject = "OBJ_1"; 1105 | } 1106 | -------------------------------------------------------------------------------- /Future.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /Future.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /Future.xcodeproj/xcshareddata/xcschemes/Future-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Quentin Jin 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 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "swift-atomics", 6 | "repositoryURL": "https://github.com/apple/swift-atomics.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "26e346cd64f6b92d4089e7fafe2f8e82f60ddb8c", 10 | "version": "0.0.2" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Future", 7 | platforms: [ 8 | .macOS(.v10_12), 9 | .iOS(.v10), 10 | .tvOS(.v10), 11 | .watchOS(.v3) 12 | ], 13 | products: [ 14 | .library(name: "Future", targets: ["Future"]) 15 | ], 16 | dependencies: [ 17 | .package(url: "https://github.com/apple/swift-atomics.git", .upToNextMinor(from: "0.0.1")) 18 | ], 19 | targets: [ 20 | .target(name: "Future", dependencies: ["Atomics"]), 21 | .testTarget(name: "FutureTests", dependencies: ["Future"]) 22 | ], 23 | swiftLanguageVersions: [ 24 | .v5 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Future.swift 2 | 3 | ![travis](https://img.shields.io/travis/luoxiu/Future.svg) 4 | ![release](https://img.shields.io/github/v/release/luoxiu/Future?include_prereleases) 5 | ![install](https://img.shields.io/badge/install-spm%20%7C%20cocoapods%20%7C%20carthage-ff69b4) 6 | ![platform](https://img.shields.io/badge/platform-ios%20%7C%20macos%20%7C%20watchos%20%7C%20tvos%20%7C%20linux-lightgrey) 7 | ![license](https://img.shields.io/github/license/luoxiu/Future.swift?color=black) 8 | 9 | 10 | `Future.swift` is a [futures and promises](https://en.wikipedia.org/wiki/Futures_and_promises) implementation for Swift. 11 | 12 | 13 | 14 | ## Highlights 15 | 16 | - **Unlimited features** 17 | - **Unmatched performance** 18 | - **Friendly api** 19 | - **Type safe** 20 | 21 | ## Benchmark 22 | 23 | The performance tests I'm using follows [google/promises](https://github.com/google/promises/blob/master/g3doc/index.md#benchmark). The comparison libraries include [promises](https://github.com/google/promises), [PromiseKit](https://github.com/mxcl/PromiseKit), [BrightFutures](https://github.com/Thomvis/BrightFutures) and [Hydra](https://github.com/malcommac/Hydra). In fact, [promises](https://github.com/google/promises) is implemented in Objective-C, but whatever. 24 | 25 | You can see [benchmark/benchmark.xcodeporj](https://github.com/luoxiu/Future.swift/tree/master/benchmark) for more information. 26 | 27 | > Average time in nanoseconds needed to create a resolved promise, chain 1/2/3 blocks and get into the last chained block on a serial queue (measured with 10,000 tries). 28 | 29 | 30 | 31 | > Average time in nanoseconds needed to resolve 10,000 pending promises with chained blocks and wait for control to get into each block on a concurrent queue. 32 | 33 | 34 | 35 | ## Usage 36 | 37 | `Future.swift`'s api is very friendly, here is a real-wold demo: 38 | 39 | ```swift 40 | func fetch(_ str: String) -> Future { 41 | guard let url = URL(string: str) else { 42 | return .failure(.invalidURL(str)) 43 | } 44 | 45 | let p = Promise() 46 | URLSession.shared.dataTask(with: url) { (data, response, error) in 47 | if let e = error { 48 | p.fail(HTTPError.session(e)) 49 | return 50 | } 51 | p.succeed(HTTPResponse(response, data)) 52 | } 53 | return p.future 54 | } 55 | 56 | let img = "https://cdn.io/me.png" 57 | fetch(img) 58 | .validate { 59 | $0.status.isValid() 60 | } 61 | .userInitiated() 62 | .tryMap { 63 | try ImageDecoder().decode($0.data) 64 | } 65 | .main { 66 | self.imageView = $0 67 | } 68 | .background { 69 | cache.add($0, for: img) 70 | } 71 | .catch { 72 | Log.error($0) 73 | } 74 | ``` 75 | 76 | `Future.swift`'s core interface is extremely simple, let's take a look: 77 | 78 | 79 | ### Future 80 | 81 | A future represents an eventual result of an asynchronous operation. 82 | 83 | - `isPending`: Return true if the future is pending. 84 | 85 | - `isCompleted`: Return true if the future is completed. 86 | 87 | - `inspect()`: Inspect the future atomically, return nil if the future is pending. 88 | 89 | - `whenComplete(_ callback: @escaping (Result) -> Void)`: Add a callback to the future that will be called when the future is completed. 90 | 91 | ### Promise 92 | 93 | A promise is responsible for managing the state of a future. 94 | 95 | ```swift 96 | let p = Promise() 97 | 98 | DispatchQueue.background { 99 | do { 100 | let r = try task() 101 | p.succeed(r) 102 | } catch let e { 103 | p.fail(e) 104 | } 105 | } 106 | 107 | p.future 108 | ``` 109 | 110 | ### Features 111 | 112 | `Future.swift` provides 30+ methods to enhance future's capabilities: 113 | 114 | - `always` 115 | - `and` 116 | - `any` 117 | - `asAny` 118 | - `asVoid` 119 | - `catch` 120 | - `delay` 121 | - `done` 122 | - `finally` 123 | - `flat` 124 | - `flatMap` 125 | - `hush` 126 | - `map` 127 | - `mute` 128 | - `pipe` 129 | - `race` 130 | - `recover` 131 | - `reduce` 132 | - `retry` 133 | - `return` 134 | - `some` 135 | - `tap` 136 | - `then` 137 | - `timeout` 138 | - `validate` 139 | - `wait` 140 | - `yield` 141 | - `whenAll` 142 | - `whenAny` 143 | - ... 144 | 145 | Detailed documentation is still being written, if you have good new ideas, welcome to contribute! 146 | 147 | ## Install 148 | 149 | ```swift 150 | dependencies: [ 151 | .package(url: "https://github.com/luoxiu/Future.swift", from: "0.0.0") 152 | ] 153 | ``` 154 | 155 | ## License 156 | 157 | MIT 158 | -------------------------------------------------------------------------------- /Sources/Future/Async.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public typealias Async = Future 4 | 5 | extension Async { 6 | 7 | public static func background(after seconds: Double = 0, body: @escaping () throws -> T) -> Future { 8 | return self.async(after: seconds, queue: .global(qos: .background), body: body) 9 | } 10 | 11 | public static func utility(after seconds: Double = 0, body: @escaping () throws -> T) -> Future { 12 | return self.async(after: seconds, queue: .global(qos: .utility), body: body) 13 | } 14 | 15 | public static func userInitiated(after seconds: Double = 0, body: @escaping () throws -> T) -> Future { 16 | return self.async(after: seconds, queue: .global(qos: .userInitiated), body: body) 17 | } 18 | 19 | public static func userInteractive(after seconds: Double = 0, body: @escaping () throws -> T) -> Future { 20 | return self.async(after: seconds, queue: .global(qos: .userInteractive), body: body) 21 | } 22 | 23 | public static func main(after seconds: Double = 0, body: @escaping () throws -> T) -> Future { 24 | return self.async(after: seconds, queue: .main, body: body) 25 | } 26 | 27 | private static func async(after seconds: Double = 0, queue: DispatchQueue, body: @escaping () throws -> T) -> Future { 28 | let p = Promise() 29 | 30 | queue.asyncAfter(deadline: .now() + seconds) { 31 | p.complete(Result(catching: body)) 32 | } 33 | 34 | return p.future 35 | } 36 | } 37 | 38 | extension Future { 39 | 40 | public func background(after seconds: Double = 0, body: @escaping (Success) -> U) -> Future { 41 | return self.delay(seconds, on: .global(qos: .background)).map(body) 42 | } 43 | 44 | public func utility(after seconds: Double = 0, body: @escaping (Success) -> U) -> Future { 45 | return self.delay(seconds, on: .global(qos: .utility)).map(body) 46 | } 47 | 48 | public func userInitiated(after seconds: Double = 0, body: @escaping (Success) -> U) -> Future { 49 | return self.delay(seconds, on: .global(qos: .userInitiated)).map(body) 50 | } 51 | 52 | public func userInteractive(after seconds: Double = 0, body: @escaping (Success) -> U) -> Future { 53 | return self.delay(seconds, on: .global(qos: .userInteractive)).map(body) 54 | } 55 | 56 | public func main(after seconds: Double = 0, body: @escaping (Success) -> U) -> Future { 57 | return self.delay(seconds, on: .main).map(body) 58 | } 59 | 60 | public func background() -> Future { 61 | return self.yield(on: .global(qos: .background)) 62 | } 63 | 64 | public func utility() -> Future { 65 | return self.yield(on: .global(qos: .utility)) 66 | } 67 | 68 | public func userInitiated() -> Future { 69 | return self.yield(on: .global(qos: .userInitiated)) 70 | } 71 | 72 | public func userInteractive() -> Future { 73 | return self.yield(on: .global(qos: .userInteractive)) 74 | } 75 | 76 | public func main() -> Future { 77 | return self.yield(on: .main) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/Future/Features/always.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func always(_ body: @escaping () -> Void) -> Future { 7 | let p = Promise() 8 | self.whenComplete { 9 | body() 10 | p.complete($0) 11 | } 12 | return p.future 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Future/Features/and.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | public func and(_ thenable: U) -> Future<(Success, U.Success), Failure> where U.Failure == Failure { 6 | return Async.whenAllSucceed(self, thenable) 7 | } 8 | 9 | public func and(_ u: U) -> Future<(Success, U), Failure> { 10 | return self.map { ($0, u) } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Future/Features/any.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | // Alias for whenAnyComplete 6 | @inlinable 7 | public static func any(_ futures: S) -> Future where S.Element: Thenable { 8 | return self.whenAnyComplete(futures) 9 | } 10 | 11 | // Alias for whenAnyComplete 12 | @inlinable 13 | public static func any(_ futures: T...) -> Future { 14 | return self.whenAnyComplete(futures) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Future/Features/asAny.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func asAny() -> Future { 7 | return self.map { $0 } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Future/Features/asVoid.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func asVoid() -> Future { 7 | return self.map { _ in () } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Future/Features/catch.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func `catch`(_ body: @escaping (Failure) -> Void) -> Future { 7 | let p = Promise() 8 | self.whenComplete { r in 9 | switch r { 10 | case .success(let s): 11 | p.succeed(s) 12 | case .failure(let e): 13 | body(e) 14 | p.fail(e) 15 | } 16 | } 17 | return p.future 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Future/Features/delay.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func delay(_ seconds: Double, on queue: DispatchQueue) -> Future { 7 | let p = Promise() 8 | self.whenComplete { r in 9 | queue.asyncAfter(deadline: .now() + seconds) { 10 | p.complete(r) 11 | } 12 | } 13 | return p.future 14 | } 15 | 16 | @inlinable 17 | public func delay(_ seconds: Double, on scheduler: TimeoutScheduler) -> Future { 18 | let p = Promise() 19 | self.whenComplete { r in 20 | scheduler.schedule(after: seconds) { 21 | p.complete(r) 22 | } 23 | } 24 | return p.future 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Future/Features/done.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func done(_ body: @escaping (Success) -> Void) -> Future { 7 | return self.map { 8 | body($0) 9 | return $0 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Future/Features/finally.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | // Alias for `always` 6 | @inlinable 7 | public func finally(_ body: @escaping () -> Void) -> Future { 8 | return self.always(body) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Future/Features/flat.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable where Success: Thenable, Success.Failure == Failure { 4 | 5 | public func flat() -> Future { 6 | return self.flatMap { $0.toFuture() } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Sources/Future/Features/flatMap.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func flatMap(_ body: @escaping (Success) -> Future) -> Future { 7 | let p = Promise() 8 | self.whenComplete { r in 9 | switch r { 10 | case .success(let s): 11 | body(s).pipe(to: p) 12 | case .failure(let e): 13 | p.fail(e) 14 | } 15 | } 16 | return p.future 17 | } 18 | 19 | @inlinable 20 | public func flatMapError(_ body: @escaping (Failure) -> Future) -> Future { 21 | let p = Promise() 22 | self.whenComplete { r in 23 | switch r { 24 | case .success(let s): 25 | p.succeed(s) 26 | case .failure(let e): 27 | body(e).pipe(to: p) 28 | } 29 | } 30 | return p.future 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Future/Features/hush.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | // 🤣 6 | @inlinable 7 | public func hush() { } 8 | } 9 | -------------------------------------------------------------------------------- /Sources/Future/Features/map.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func map(_ body: @escaping (Success) -> U) -> Future { 7 | let p = Promise() 8 | self.whenComplete { r in 9 | switch r { 10 | case .success(let s): 11 | p.succeed(body(s)) 12 | case .failure(let e): 13 | p.fail(e) 14 | } 15 | } 16 | return p.future 17 | } 18 | 19 | @inlinable 20 | public func tryMap(_ body: @escaping (Success) throws -> U) -> Future { 21 | let p = Promise() 22 | self.whenComplete { r in 23 | switch r { 24 | case .success(let s): 25 | do { 26 | p.succeed(try body(s)) 27 | } catch let e { 28 | p.fail(e) 29 | } 30 | case .failure(let e): 31 | p.fail(e) 32 | } 33 | } 34 | return p.future 35 | } 36 | 37 | @inlinable 38 | public func mapError(_ body: @escaping (Error) -> E) -> Future { 39 | let p = Promise() 40 | self.whenComplete { r in 41 | switch r { 42 | case .success(let s): 43 | p.succeed(s) 44 | case .failure(let e): 45 | p.fail(body(e)) 46 | } 47 | } 48 | return p.future 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Future/Features/mute.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func mute() { } 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Future/Features/pipe.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func pipe(to promise: Promise) { 7 | self.whenComplete(promise.complete) 8 | } 9 | 10 | @inlinable 11 | public func pipeSuccess(to promise: Promise) { 12 | self.whenSucceed(promise.succeed) 13 | } 14 | 15 | @inlinable 16 | public func pipeFailure(to promise: Promise) { 17 | self.whenFail(promise.fail) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Future/Features/race.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | // Alias for `whenAnyComplete`. 6 | @inlinable 7 | public static func race(_ futures: [T]) -> Future { 8 | return self.whenAnyComplete(futures) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Future/Features/recover.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func recover(_ body: @escaping (Error) -> Success) -> Future { 7 | let p = Promise() 8 | 9 | self.whenComplete { r in 10 | switch r { 11 | case .success(let s): 12 | p.succeed(s) 13 | case .failure(let e): 14 | p.succeed(body(e)) 15 | } 16 | } 17 | 18 | return p.future 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Future/Features/reduce.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public static func reduce(_ thenables: S, initial: U, nextPartial: @escaping (U, S.Element.Success) -> Future) -> Future where S.Element: Thenable { 7 | return self.reduce(thenables, initial: Future.success(initial), nextPartial: nextPartial) 8 | } 9 | 10 | @inlinable 11 | public static func reduce(_ thenables: S, initial: U, nextPartial: @escaping (U.Success, S.Element.Success) -> Future) -> Future where S.Element: Thenable, S.Element.Failure == U.Failure { 12 | return thenables.reduce(initial.toFuture()) { (fu, ft) -> Future in 13 | return whenAllSucceed(fu, ft).flatMap { (ut) in 14 | return nextPartial(ut.0, ut.1) 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Future/Features/retry.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public static func retry(count: Int, task: @escaping () -> Future) -> Future { 7 | return self._retry(count: count, task: task) 8 | } 9 | 10 | @inlinable 11 | static func _retry(count: Int, task: @escaping () -> Future) -> Future { 12 | return task().flatMapError { 13 | if count == 0 { 14 | return Future.failure($0) 15 | } else { 16 | return _retry(count: count - 1, task: task) 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Future/Features/return.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Future { 4 | 5 | // Alias for thenReturn 6 | @inlinable 7 | public func `return`(_ body: @escaping (Success) -> U) -> Future { 8 | return self.map(body) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Future/Features/some.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public static func some(_ thenables: S, count: Int) -> Future<[S.Element.Success], S.Element.Failure> where S.Element: Thenable { 7 | let p = Promise<[S.Element.Success], S.Element.Failure>() 8 | 9 | var vals: [S.Element.Success] = [] 10 | vals.reserveCapacity(count) 11 | 12 | let atomicVals = Atom(vals) 13 | 14 | for f in thenables { 15 | f.whenSucceed { s in 16 | let after = atomicVals.append(s) 17 | if after.count == count { 18 | p.succeed(after) 19 | } 20 | } 21 | } 22 | 23 | return p.future 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Future/Features/tap.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func tap(_ body: @escaping (Success) -> Void) -> Future { 7 | let p = Promise() 8 | 9 | self.whenComplete { 10 | if case .success(let s) = $0 { 11 | body(s) 12 | } 13 | p.complete($0) 14 | } 15 | 16 | return p.future 17 | } 18 | 19 | @inlinable 20 | public func tapError(_ body: @escaping (Failure) -> Void) -> Future { 21 | let p = Promise() 22 | 23 | self.whenComplete { 24 | if case .failure(let e) = $0 { 25 | body(e) 26 | } 27 | p.complete($0) 28 | } 29 | 30 | return p.future 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Future/Features/then.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | // Alias for whenComplete 6 | @inlinable 7 | public func then(_ callback: @escaping (Result) -> Void) { 8 | self.whenComplete(callback) 9 | } 10 | 11 | // Alias for flatMap 12 | @inlinable 13 | public func then(_ body: @escaping (Success) -> Future) -> Future { 14 | return self.flatMap(body) 15 | } 16 | 17 | // Alias for map 18 | @inlinable 19 | public func thenReturn(_ body: @escaping (Success) -> U) -> Future { 20 | return self.map(body) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Future/Features/timeout.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func timeout(_ seconds: Double, on queue: DispatchQueue, whenTimeout: @autoclosure @escaping () -> Failure) -> Future { 7 | let p = Promise() 8 | queue.asyncAfter(deadline: .now() + seconds) { 9 | p.fail(whenTimeout()) 10 | } 11 | 12 | self.whenComplete { r in 13 | queue.async { 14 | switch r { 15 | case .success(let s): 16 | p.succeed(s) 17 | case .failure(let e): 18 | p.fail(e) 19 | } 20 | } 21 | } 22 | 23 | return p.future 24 | } 25 | 26 | @inlinable 27 | public func timeout(_ seconds: Double, on scheduler: TimeoutScheduler, whenTimeout: @autoclosure @escaping () -> Failure) -> Future { 28 | let p = Promise() 29 | scheduler.schedule(after: seconds) { 30 | p.fail(whenTimeout()) 31 | } 32 | 33 | self.whenComplete { r in 34 | scheduler.schedule { 35 | switch r { 36 | case .success(let s): 37 | p.succeed(s) 38 | case .failure(let e): 39 | p.fail(e) 40 | } 41 | } 42 | } 43 | 44 | return p.future 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Future/Features/validate.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func validate(_ body: @escaping (Success) -> Bool, whenFail: @autoclosure @escaping () -> Failure) -> Future { 7 | 8 | let p = Promise() 9 | 10 | self.whenComplete { r in 11 | switch r { 12 | case .success(let s): 13 | if body(s) { 14 | p.succeed(s) 15 | } else { 16 | p.fail(whenFail()) 17 | } 18 | case .failure(let e): 19 | p.fail(e) 20 | } 21 | } 22 | 23 | return p.future 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Future/Features/wait.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public func wait() -> Success? { 7 | let sema = DispatchSemaphore(value: 0) 8 | self.whenComplete { _ in 9 | sema.signal() 10 | } 11 | sema.wait() 12 | 13 | return self.inspectWithoutLock()!.value 14 | } 15 | 16 | @inlinable 17 | public func waitError() -> Failure? { 18 | let sema = DispatchSemaphore(value: 0) 19 | self.whenComplete { _ in 20 | sema.signal() 21 | } 22 | sema.wait() 23 | 24 | return self.inspectWithoutLock()!.error 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAll.T.gyb: -------------------------------------------------------------------------------- 1 | /* 2 | ⚠️️️️⚠️️️️⚠️️️️ 3 | This file was generated from `./whenAll.T.gyb`, you shouldn't modify it directly. 4 | ./utils/gyb.py ./Sources/Future/Features/whenAll.T.gyb -o ./Sources/Future/Features/whenAll.T.swift --line-directive '' 5 | */ 6 | %{ 7 | r = range(2, 8) 8 | 9 | def typeList(i): 10 | names = map(lambda x: "T%d: Thenable" % x, range(1, i + 1)) 11 | return ", ".join(names) 12 | 13 | def paramList(i): 14 | names = map(lambda x: "_ thenable%d: T%d" % (x, x), range(1, i + 1)) 15 | return ",\n\t\t".join(names) 16 | 17 | def whereList(i): 18 | names = map(lambda x: "T%d.Failure == T%d.Failure" % (x, x + 1), range(1, i)) 19 | return ", ".join(names) 20 | 21 | def resultList(i): 22 | names = map(lambda x: "Result" % (x, x), range(1, i + 1)) 23 | return ", ".join(names) 24 | 25 | def valueList(i): 26 | names = map(lambda x: "T%d.Success" % x, range(1, i + 1)) 27 | return ", ".join(names) 28 | 29 | def asVoidList(i): 30 | names = map(lambda x: "thenable%d.asVoid()" % x, range(1, i + 1)) 31 | return ", ".join(names) 32 | 33 | def getResultList(i): 34 | names = map(lambda x: "thenable%d.inspectRoughly()!" % x, range(1, i + 1)) 35 | return ", ".join(names) 36 | 37 | def getValueList(i): 38 | names = map(lambda x: "thenable%d.inspectRoughly()!.value!" % x, range(1, i + 1)) 39 | return ", ".join(names) 40 | }% 41 | import Foundation 42 | 43 | extension Thenable { 44 | 45 | % for i in r: 46 | @inlinable 47 | public static func whenAllComplete<${typeList(i)}>( 48 | ${paramList(i)} 49 | ) 50 | -> Future<(${resultList(i)}), T1.Failure> 51 | where ${whereList(i)} 52 | { 53 | return self.whenAllSucceedVoid( 54 | [${asVoidList(i)}] 55 | ) 56 | .map { _ in 57 | (${getResultList(i)}) 58 | } 59 | } 60 | 61 | % end 62 | % for i in r: 63 | @inlinable 64 | public static func whenAllSucceed<${typeList(i)}>( 65 | ${paramList(i)} 66 | ) 67 | -> Future<(${valueList(i)}), T1.Failure> 68 | where ${whereList(i)} 69 | { 70 | return self.whenAllSucceedVoid( 71 | [${asVoidList(i)}] 72 | ) 73 | .map { _ in 74 | (${getValueList(i)}) 75 | } 76 | } 77 | 78 | % end 79 | } 80 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAll.T.swift: -------------------------------------------------------------------------------- 1 | /* 2 | ⚠️️️️⚠️️️️⚠️️️️ 3 | This file was generated from `./whenAll.T.gyb`, you shouldn't modify it directly. 4 | ./utils/gyb.py ./Sources/FutureQ/Features/whenAll.T.gyb -o ./Sources/FutureQ/Features/whenAll.T.swift --line-directive '' 5 | */ 6 | import Foundation 7 | 8 | extension Thenable { 9 | 10 | @inlinable 11 | public static func whenAllComplete( 12 | _ thenable1: T1, 13 | _ thenable2: T2 14 | ) 15 | -> Future<(Result, Result), T1.Failure> 16 | where T1.Failure == T2.Failure 17 | { 18 | return self.whenAllSucceedVoid( 19 | [thenable1.asVoid(), thenable2.asVoid()] 20 | ) 21 | .map { _ in 22 | (thenable1.inspectWithoutLock()!, thenable2.inspectWithoutLock()!) 23 | } 24 | } 25 | 26 | @inlinable 27 | public static func whenAllComplete( 28 | _ thenable1: T1, 29 | _ thenable2: T2, 30 | _ thenable3: T3 31 | ) 32 | -> Future<(Result, Result, Result), T1.Failure> 33 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure 34 | { 35 | return self.whenAllSucceedVoid( 36 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid()] 37 | ) 38 | .map { _ in 39 | (thenable1.inspectWithoutLock()!, thenable2.inspectWithoutLock()!, thenable3.inspectWithoutLock()!) 40 | } 41 | } 42 | 43 | @inlinable 44 | public static func whenAllComplete( 45 | _ thenable1: T1, 46 | _ thenable2: T2, 47 | _ thenable3: T3, 48 | _ thenable4: T4 49 | ) 50 | -> Future<(Result, Result, Result, Result), T1.Failure> 51 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure 52 | { 53 | return self.whenAllSucceedVoid( 54 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid()] 55 | ) 56 | .map { _ in 57 | (thenable1.inspectWithoutLock()!, thenable2.inspectWithoutLock()!, thenable3.inspectWithoutLock()!, thenable4.inspectWithoutLock()!) 58 | } 59 | } 60 | 61 | @inlinable 62 | public static func whenAllComplete( 63 | _ thenable1: T1, 64 | _ thenable2: T2, 65 | _ thenable3: T3, 66 | _ thenable4: T4, 67 | _ thenable5: T5 68 | ) 69 | -> Future<(Result, Result, Result, Result, Result), T1.Failure> 70 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure 71 | { 72 | return self.whenAllSucceedVoid( 73 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid()] 74 | ) 75 | .map { _ in 76 | (thenable1.inspectWithoutLock()!, thenable2.inspectWithoutLock()!, thenable3.inspectWithoutLock()!, thenable4.inspectWithoutLock()!, thenable5.inspectWithoutLock()!) 77 | } 78 | } 79 | 80 | @inlinable 81 | public static func whenAllComplete( 82 | _ thenable1: T1, 83 | _ thenable2: T2, 84 | _ thenable3: T3, 85 | _ thenable4: T4, 86 | _ thenable5: T5, 87 | _ thenable6: T6 88 | ) 89 | -> Future<(Result, Result, Result, Result, Result, Result), T1.Failure> 90 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure 91 | { 92 | return self.whenAllSucceedVoid( 93 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid()] 94 | ) 95 | .map { _ in 96 | (thenable1.inspectWithoutLock()!, thenable2.inspectWithoutLock()!, thenable3.inspectWithoutLock()!, thenable4.inspectWithoutLock()!, thenable5.inspectWithoutLock()!, thenable6.inspectWithoutLock()!) 97 | } 98 | } 99 | 100 | @inlinable 101 | public static func whenAllComplete( 102 | _ thenable1: T1, 103 | _ thenable2: T2, 104 | _ thenable3: T3, 105 | _ thenable4: T4, 106 | _ thenable5: T5, 107 | _ thenable6: T6, 108 | _ thenable7: T7 109 | ) 110 | -> Future<(Result, Result, Result, Result, Result, Result, Result), T1.Failure> 111 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure, T6.Failure == T7.Failure 112 | { 113 | return self.whenAllSucceedVoid( 114 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid(), thenable7.asVoid()] 115 | ) 116 | .map { _ in 117 | (thenable1.inspectWithoutLock()!, thenable2.inspectWithoutLock()!, thenable3.inspectWithoutLock()!, thenable4.inspectWithoutLock()!, thenable5.inspectWithoutLock()!, thenable6.inspectWithoutLock()!, thenable7.inspectWithoutLock()!) 118 | } 119 | } 120 | 121 | @inlinable 122 | public static func whenAllSucceed( 123 | _ thenable1: T1, 124 | _ thenable2: T2 125 | ) 126 | -> Future<(T1.Success, T2.Success), T1.Failure> 127 | where T1.Failure == T2.Failure 128 | { 129 | return self.whenAllSucceedVoid( 130 | [thenable1.asVoid(), thenable2.asVoid()] 131 | ) 132 | .map { _ in 133 | (thenable1.inspectWithoutLock()!.value!, thenable2.inspectWithoutLock()!.value!) 134 | } 135 | } 136 | 137 | @inlinable 138 | public static func whenAllSucceed( 139 | _ thenable1: T1, 140 | _ thenable2: T2, 141 | _ thenable3: T3 142 | ) 143 | -> Future<(T1.Success, T2.Success, T3.Success), T1.Failure> 144 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure 145 | { 146 | return self.whenAllSucceedVoid( 147 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid()] 148 | ) 149 | .map { _ in 150 | (thenable1.inspectWithoutLock()!.value!, thenable2.inspectWithoutLock()!.value!, thenable3.inspectWithoutLock()!.value!) 151 | } 152 | } 153 | 154 | @inlinable 155 | public static func whenAllSucceed( 156 | _ thenable1: T1, 157 | _ thenable2: T2, 158 | _ thenable3: T3, 159 | _ thenable4: T4 160 | ) 161 | -> Future<(T1.Success, T2.Success, T3.Success, T4.Success), T1.Failure> 162 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure 163 | { 164 | return self.whenAllSucceedVoid( 165 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid()] 166 | ) 167 | .map { _ in 168 | (thenable1.inspectWithoutLock()!.value!, thenable2.inspectWithoutLock()!.value!, thenable3.inspectWithoutLock()!.value!, thenable4.inspectWithoutLock()!.value!) 169 | } 170 | } 171 | 172 | @inlinable 173 | public static func whenAllSucceed( 174 | _ thenable1: T1, 175 | _ thenable2: T2, 176 | _ thenable3: T3, 177 | _ thenable4: T4, 178 | _ thenable5: T5 179 | ) 180 | -> Future<(T1.Success, T2.Success, T3.Success, T4.Success, T5.Success), T1.Failure> 181 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure 182 | { 183 | return self.whenAllSucceedVoid( 184 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid()] 185 | ) 186 | .map { _ in 187 | (thenable1.inspectWithoutLock()!.value!, thenable2.inspectWithoutLock()!.value!, thenable3.inspectWithoutLock()!.value!, thenable4.inspectWithoutLock()!.value!, thenable5.inspectWithoutLock()!.value!) 188 | } 189 | } 190 | 191 | @inlinable 192 | public static func whenAllSucceed( 193 | _ thenable1: T1, 194 | _ thenable2: T2, 195 | _ thenable3: T3, 196 | _ thenable4: T4, 197 | _ thenable5: T5, 198 | _ thenable6: T6 199 | ) 200 | -> Future<(T1.Success, T2.Success, T3.Success, T4.Success, T5.Success, T6.Success), T1.Failure> 201 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure 202 | { 203 | return self.whenAllSucceedVoid( 204 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid()] 205 | ) 206 | .map { _ in 207 | (thenable1.inspectWithoutLock()!.value!, thenable2.inspectWithoutLock()!.value!, thenable3.inspectWithoutLock()!.value!, thenable4.inspectWithoutLock()!.value!, thenable5.inspectWithoutLock()!.value!, thenable6.inspectWithoutLock()!.value!) 208 | } 209 | } 210 | 211 | @inlinable 212 | public static func whenAllSucceed( 213 | _ thenable1: T1, 214 | _ thenable2: T2, 215 | _ thenable3: T3, 216 | _ thenable4: T4, 217 | _ thenable5: T5, 218 | _ thenable6: T6, 219 | _ thenable7: T7 220 | ) 221 | -> Future<(T1.Success, T2.Success, T3.Success, T4.Success, T5.Success, T6.Success, T7.Success), T1.Failure> 222 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure, T6.Failure == T7.Failure 223 | { 224 | return self.whenAllSucceedVoid( 225 | [thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid(), thenable7.asVoid()] 226 | ) 227 | .map { _ in 228 | (thenable1.inspectWithoutLock()!.value!, thenable2.inspectWithoutLock()!.value!, thenable3.inspectWithoutLock()!.value!, thenable4.inspectWithoutLock()!.value!, thenable5.inspectWithoutLock()!.value!, thenable6.inspectWithoutLock()!.value!, thenable7.inspectWithoutLock()!.value!) 229 | } 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAll.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public static func whenAllComplete(_ thenables: C) -> Future<[Result], C.Element.Failure> where C.Element: Thenable { 7 | let p = Promise<[Result], C.Element.Failure>() 8 | 9 | let count = Atom(thenables.count) 10 | 11 | for t in thenables { 12 | t.whenComplete { _ in 13 | if count.sub(1) == 0 { 14 | p.succeed(thenables.map({ $0.inspectWithoutLock()! })) 15 | } 16 | } 17 | } 18 | 19 | return p.future 20 | } 21 | 22 | @inlinable 23 | public static func whenAllComplete(_ thenables: T...) -> Future<[Result], T.Failure> { 24 | return self.whenAllComplete(thenables) 25 | } 26 | 27 | @inlinable 28 | public static func whenAllSucceed(_ thenables: C) -> Future<[C.Element.Success], C.Element.Failure> where C.Element: Thenable { 29 | let p = Promise<[C.Element.Success], C.Element.Failure>() 30 | 31 | let count = Atom(thenables.count) 32 | 33 | for t in thenables { 34 | t.whenComplete { r in 35 | switch r { 36 | case .success: 37 | if count.sub(1) == 0 { 38 | p.succeed(thenables.map({ $0.inspectWithoutLock()!.value! })) 39 | } 40 | case .failure(let e): 41 | p.fail(e) 42 | } 43 | } 44 | } 45 | 46 | return p.future 47 | } 48 | 49 | @inlinable 50 | public static func whenAllSucceed(_ thenables: T...) -> Future<[T.Success], T.Failure> { 51 | return self.whenAllSucceed(thenables) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAllVoid.T.gyb: -------------------------------------------------------------------------------- 1 | /* 2 | ⚠️️️️⚠️️️️⚠️️️️ 3 | This file was generated from `./whenAllVoid.T.gyb`, you shouldn't modify it directly. 4 | ./utils/gyb.py ./Sources/Future/Features/whenAllVoid.T.gyb -o ./Sources/Future/Features/whenAllVoid.T.swift --line-directive '' 5 | */ 6 | %{ 7 | r = range(2, 8) 8 | 9 | def typeList(i): 10 | names = map(lambda x: "T%d: Thenable" % x, range(1, i + 1)) 11 | return ", ".join(names) 12 | 13 | def paramList(i): 14 | names = map(lambda x: "_ thenable%d: T%d" % (x, x), range(1, i + 1)) 15 | return ",\n\t\t".join(names) 16 | 17 | def whereList(i): 18 | names = map(lambda x: "T%d.Failure == T%d.Failure" % (x, x + 1), range(1, i)) 19 | return ", ".join(names) 20 | 21 | def asVoidList(i): 22 | names = map(lambda x: "thenable%d.asVoid()" % x, range(1, i + 1)) 23 | return ", ".join(names) 24 | }% 25 | 26 | import Foundation 27 | 28 | extension Thenable { 29 | 30 | % for i in r: 31 | @inlinable 32 | public static func whenAllCompleteVoid<${typeList(i)}>( 33 | ${paramList(i)} 34 | ) 35 | -> Future 36 | where ${whereList(i)} 37 | { 38 | return self.whenAllCompleteVoid([${asVoidList(i)}]) 39 | } 40 | 41 | % end 42 | % for i in r: 43 | @inlinable 44 | public static func whenAllSucceedVoid<${typeList(i)}>( 45 | ${paramList(i)} 46 | ) 47 | -> Future 48 | where ${whereList(i)} 49 | { 50 | return self.whenAllSucceedVoid([${asVoidList(i)}]) 51 | } 52 | 53 | % end 54 | } 55 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAllVoid.T.swift: -------------------------------------------------------------------------------- 1 | /* 2 | ⚠️️️️⚠️️️️⚠️️️️ 3 | This file was generated from `./whenAllVoid.T.gyb`, you shouldn't modify it directly. 4 | ./utils/gyb.py ./Sources/FutureQ/Features/whenAllVoid.T.gyb -o ./Sources/FutureQ/Features/whenAllVoid.T.swift --line-directive '' 5 | */ 6 | 7 | import Foundation 8 | 9 | extension Thenable { 10 | 11 | @inlinable 12 | public static func whenAllCompleteVoid( 13 | _ thenable1: T1, 14 | _ thenable2: T2 15 | ) 16 | -> Future 17 | where T1.Failure == T2.Failure 18 | { 19 | return self.whenAllCompleteVoid([thenable1.asVoid(), thenable2.asVoid()]) 20 | } 21 | 22 | @inlinable 23 | public static func whenAllCompleteVoid( 24 | _ thenable1: T1, 25 | _ thenable2: T2, 26 | _ thenable3: T3 27 | ) 28 | -> Future 29 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure 30 | { 31 | return self.whenAllCompleteVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid()]) 32 | } 33 | 34 | @inlinable 35 | public static func whenAllCompleteVoid( 36 | _ thenable1: T1, 37 | _ thenable2: T2, 38 | _ thenable3: T3, 39 | _ thenable4: T4 40 | ) 41 | -> Future 42 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure 43 | { 44 | return self.whenAllCompleteVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid()]) 45 | } 46 | 47 | @inlinable 48 | public static func whenAllCompleteVoid( 49 | _ thenable1: T1, 50 | _ thenable2: T2, 51 | _ thenable3: T3, 52 | _ thenable4: T4, 53 | _ thenable5: T5 54 | ) 55 | -> Future 56 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure 57 | { 58 | return self.whenAllCompleteVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid()]) 59 | } 60 | 61 | @inlinable 62 | public static func whenAllCompleteVoid( 63 | _ thenable1: T1, 64 | _ thenable2: T2, 65 | _ thenable3: T3, 66 | _ thenable4: T4, 67 | _ thenable5: T5, 68 | _ thenable6: T6 69 | ) 70 | -> Future 71 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure 72 | { 73 | return self.whenAllCompleteVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid()]) 74 | } 75 | 76 | @inlinable 77 | public static func whenAllCompleteVoid( 78 | _ thenable1: T1, 79 | _ thenable2: T2, 80 | _ thenable3: T3, 81 | _ thenable4: T4, 82 | _ thenable5: T5, 83 | _ thenable6: T6, 84 | _ thenable7: T7 85 | ) 86 | -> Future 87 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure, T6.Failure == T7.Failure 88 | { 89 | return self.whenAllCompleteVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid(), thenable7.asVoid()]) 90 | } 91 | 92 | @inlinable 93 | public static func whenAllSucceedVoid( 94 | _ thenable1: T1, 95 | _ thenable2: T2 96 | ) 97 | -> Future 98 | where T1.Failure == T2.Failure 99 | { 100 | return self.whenAllSucceedVoid([thenable1.asVoid(), thenable2.asVoid()]) 101 | } 102 | 103 | @inlinable 104 | public static func whenAllSucceedVoid( 105 | _ thenable1: T1, 106 | _ thenable2: T2, 107 | _ thenable3: T3 108 | ) 109 | -> Future 110 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure 111 | { 112 | return self.whenAllSucceedVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid()]) 113 | } 114 | 115 | @inlinable 116 | public static func whenAllSucceedVoid( 117 | _ thenable1: T1, 118 | _ thenable2: T2, 119 | _ thenable3: T3, 120 | _ thenable4: T4 121 | ) 122 | -> Future 123 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure 124 | { 125 | return self.whenAllSucceedVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid()]) 126 | } 127 | 128 | @inlinable 129 | public static func whenAllSucceedVoid( 130 | _ thenable1: T1, 131 | _ thenable2: T2, 132 | _ thenable3: T3, 133 | _ thenable4: T4, 134 | _ thenable5: T5 135 | ) 136 | -> Future 137 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure 138 | { 139 | return self.whenAllSucceedVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid()]) 140 | } 141 | 142 | @inlinable 143 | public static func whenAllSucceedVoid( 144 | _ thenable1: T1, 145 | _ thenable2: T2, 146 | _ thenable3: T3, 147 | _ thenable4: T4, 148 | _ thenable5: T5, 149 | _ thenable6: T6 150 | ) 151 | -> Future 152 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure 153 | { 154 | return self.whenAllSucceedVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid()]) 155 | } 156 | 157 | @inlinable 158 | public static func whenAllSucceedVoid( 159 | _ thenable1: T1, 160 | _ thenable2: T2, 161 | _ thenable3: T3, 162 | _ thenable4: T4, 163 | _ thenable5: T5, 164 | _ thenable6: T6, 165 | _ thenable7: T7 166 | ) 167 | -> Future 168 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure, T6.Failure == T7.Failure 169 | { 170 | return self.whenAllSucceedVoid([thenable1.asVoid(), thenable2.asVoid(), thenable3.asVoid(), thenable4.asVoid(), thenable5.asVoid(), thenable6.asVoid(), thenable7.asVoid()]) 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAllVoid.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // For performance 4 | 5 | extension Thenable { 6 | 7 | @inlinable 8 | public static func whenAllCompleteVoid(_ thenables: C) -> Future where C.Element: Thenable { 9 | let p = Promise() 10 | 11 | let count = Atom(thenables.count) 12 | 13 | for t in thenables { 14 | t.whenComplete { _ in 15 | if count.sub(1) == 0 { 16 | p.succeed(()) 17 | } 18 | } 19 | } 20 | 21 | return p.future 22 | } 23 | 24 | @inlinable 25 | public static func whenAllCompleteVoid(_ thenables: T...) -> Future { 26 | return self.whenAllCompleteVoid(thenables) 27 | } 28 | 29 | @inlinable 30 | public static func whenAllSucceedVoid(_ thenables: C) -> Future where C.Element: Thenable { 31 | let p = Promise() 32 | 33 | let count = Atom(thenables.count) 34 | 35 | for t in thenables { 36 | t.whenComplete { r in 37 | switch r { 38 | case .success: 39 | if count.sub(1) == 0 { 40 | p.succeed(()) 41 | } 42 | case .failure(let e): 43 | p.fail(e) 44 | } 45 | } 46 | } 47 | 48 | return p.future 49 | } 50 | 51 | @inlinable 52 | public static func whenAllSucceedVoid(_ thenables: T...) -> Future { 53 | return self.whenAllSucceedVoid(thenables) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAny.T.gyb: -------------------------------------------------------------------------------- 1 | /* 2 | ⚠️️️️⚠️️️️⚠️️️️ 3 | This file was generated from `./whenAny.T.gyb`, you shouldn't modify it directly. 4 | ./utils/gyb.py ./Sources/Future/Features/whenAny.T.gyb -o ./Sources/Future/Features/whenAny.T.swift --line-directive '' 5 | */ 6 | %{ 7 | r = range(2, 8) 8 | 9 | def typeList(i): 10 | names = map(lambda x: "T%d: Thenable" % x, range(1, i + 1)) 11 | return ", ".join(names) 12 | 13 | def paramList(i): 14 | names = map(lambda x: "_ thenable%d: T%d" % (x, x), range(1, i + 1)) 15 | return ",\n\t\t".join(names) 16 | 17 | def whereList(i): 18 | names = map(lambda x: "T%d.Failure == T%d.Failure" % (x, x + 1), range(1, i)) 19 | return ", ".join(names) 20 | 21 | def asAnyList(i): 22 | names = map(lambda x: "thenable%d.asAny()" % x, range(1, i + 1)) 23 | return ", ".join(names) 24 | }% 25 | 26 | import Foundation 27 | 28 | extension Thenable { 29 | 30 | % for i in r: 31 | @inlinable 32 | public static func whenAnyComplete<${typeList(i)}>( 33 | ${paramList(i)} 34 | ) 35 | -> Future 36 | where ${whereList(i)} 37 | { 38 | return self.whenAnyComplete([${asAnyList(i)}]) 39 | } 40 | 41 | % end 42 | % for i in r: 43 | @inlinable 44 | public static func whenAnySucceed<${typeList(i)}>( 45 | ${paramList(i)} 46 | ) 47 | -> Future 48 | where ${whereList(i)} 49 | { 50 | return self.whenAnySucceed([${asAnyList(i)}]) 51 | } 52 | 53 | % end 54 | } 55 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAny.T.swift: -------------------------------------------------------------------------------- 1 | /* 2 | ⚠️️️️⚠️️️️⚠️️️️ 3 | This file was generated from `./whenAny.T.gyb`, you shouldn't modify it directly. 4 | ./utils/gyb.py ./Sources/FutureQ/Features/whenAny.T.gyb -o ./Sources/FutureQ/Features/whenAny.T.swift --line-directive '' 5 | */ 6 | 7 | import Foundation 8 | 9 | extension Thenable { 10 | 11 | @inlinable 12 | public static func whenAnyComplete( 13 | _ thenable1: T1, 14 | _ thenable2: T2 15 | ) 16 | -> Future 17 | where T1.Failure == T2.Failure 18 | { 19 | return self.whenAnyComplete([thenable1.asAny(), thenable2.asAny()]) 20 | } 21 | 22 | @inlinable 23 | public static func whenAnyComplete( 24 | _ thenable1: T1, 25 | _ thenable2: T2, 26 | _ thenable3: T3 27 | ) 28 | -> Future 29 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure 30 | { 31 | return self.whenAnyComplete([thenable1.asAny(), thenable2.asAny(), thenable3.asAny()]) 32 | } 33 | 34 | @inlinable 35 | public static func whenAnyComplete( 36 | _ thenable1: T1, 37 | _ thenable2: T2, 38 | _ thenable3: T3, 39 | _ thenable4: T4 40 | ) 41 | -> Future 42 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure 43 | { 44 | return self.whenAnyComplete([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny()]) 45 | } 46 | 47 | @inlinable 48 | public static func whenAnyComplete( 49 | _ thenable1: T1, 50 | _ thenable2: T2, 51 | _ thenable3: T3, 52 | _ thenable4: T4, 53 | _ thenable5: T5 54 | ) 55 | -> Future 56 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure 57 | { 58 | return self.whenAnyComplete([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny(), thenable5.asAny()]) 59 | } 60 | 61 | @inlinable 62 | public static func whenAnyComplete( 63 | _ thenable1: T1, 64 | _ thenable2: T2, 65 | _ thenable3: T3, 66 | _ thenable4: T4, 67 | _ thenable5: T5, 68 | _ thenable6: T6 69 | ) 70 | -> Future 71 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure 72 | { 73 | return self.whenAnyComplete([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny(), thenable5.asAny(), thenable6.asAny()]) 74 | } 75 | 76 | @inlinable 77 | public static func whenAnyComplete( 78 | _ thenable1: T1, 79 | _ thenable2: T2, 80 | _ thenable3: T3, 81 | _ thenable4: T4, 82 | _ thenable5: T5, 83 | _ thenable6: T6, 84 | _ thenable7: T7 85 | ) 86 | -> Future 87 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure, T6.Failure == T7.Failure 88 | { 89 | return self.whenAnyComplete([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny(), thenable5.asAny(), thenable6.asAny(), thenable7.asAny()]) 90 | } 91 | 92 | @inlinable 93 | public static func whenAnySucceed( 94 | _ thenable1: T1, 95 | _ thenable2: T2 96 | ) 97 | -> Future 98 | where T1.Failure == T2.Failure 99 | { 100 | return self.whenAnySucceed([thenable1.asAny(), thenable2.asAny()]) 101 | } 102 | 103 | @inlinable 104 | public static func whenAnySucceed( 105 | _ thenable1: T1, 106 | _ thenable2: T2, 107 | _ thenable3: T3 108 | ) 109 | -> Future 110 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure 111 | { 112 | return self.whenAnySucceed([thenable1.asAny(), thenable2.asAny(), thenable3.asAny()]) 113 | } 114 | 115 | @inlinable 116 | public static func whenAnySucceed( 117 | _ thenable1: T1, 118 | _ thenable2: T2, 119 | _ thenable3: T3, 120 | _ thenable4: T4 121 | ) 122 | -> Future 123 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure 124 | { 125 | return self.whenAnySucceed([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny()]) 126 | } 127 | 128 | @inlinable 129 | public static func whenAnySucceed( 130 | _ thenable1: T1, 131 | _ thenable2: T2, 132 | _ thenable3: T3, 133 | _ thenable4: T4, 134 | _ thenable5: T5 135 | ) 136 | -> Future 137 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure 138 | { 139 | return self.whenAnySucceed([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny(), thenable5.asAny()]) 140 | } 141 | 142 | @inlinable 143 | public static func whenAnySucceed( 144 | _ thenable1: T1, 145 | _ thenable2: T2, 146 | _ thenable3: T3, 147 | _ thenable4: T4, 148 | _ thenable5: T5, 149 | _ thenable6: T6 150 | ) 151 | -> Future 152 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure 153 | { 154 | return self.whenAnySucceed([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny(), thenable5.asAny(), thenable6.asAny()]) 155 | } 156 | 157 | @inlinable 158 | public static func whenAnySucceed( 159 | _ thenable1: T1, 160 | _ thenable2: T2, 161 | _ thenable3: T3, 162 | _ thenable4: T4, 163 | _ thenable5: T5, 164 | _ thenable6: T6, 165 | _ thenable7: T7 166 | ) 167 | -> Future 168 | where T1.Failure == T2.Failure, T2.Failure == T3.Failure, T3.Failure == T4.Failure, T4.Failure == T5.Failure, T5.Failure == T6.Failure, T6.Failure == T7.Failure 169 | { 170 | return self.whenAnySucceed([thenable1.asAny(), thenable2.asAny(), thenable3.asAny(), thenable4.asAny(), thenable5.asAny(), thenable6.asAny(), thenable7.asAny()]) 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /Sources/Future/Features/whenAny.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | @inlinable 6 | public static func whenAnyComplete(_ thenables: S) -> Future where S.Element: Thenable { 7 | let p = Promise() 8 | for t in thenables { 9 | t.pipe(to: p) 10 | } 11 | return p.future 12 | } 13 | 14 | @inlinable 15 | public static func whenAnyComplete(_ thenables: T...) -> Future { 16 | return self.whenAnyComplete(thenables) 17 | } 18 | 19 | @inlinable 20 | public static func whenAnySucceed(_ thenables: S) -> Future where S.Element: Thenable { 21 | let p = Promise() 22 | for t in thenables { 23 | t.pipeSuccess(to: p) 24 | } 25 | return p.future 26 | } 27 | 28 | @inlinable 29 | public static func whenAnySucceed(_ thenabls: T...) -> Future { 30 | return self.whenAnySucceed(thenabls) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Future/Features/yield.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Thenable { 4 | 5 | public func yield(on queue: DispatchQueue) -> Future { 6 | let p = Promise() 7 | 8 | self.whenComplete { r in 9 | queue.async { 10 | p.complete(r) 11 | } 12 | } 13 | 14 | return p.future 15 | } 16 | 17 | public func yield(on scheduler: Scheduler) -> Future { 18 | let p = Promise() 19 | 20 | self.whenComplete { r in 21 | scheduler.schedule { 22 | p.complete(r) 23 | } 24 | } 25 | 26 | return p.future 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Future/Future.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A Future represents an eventual result of an asynchronous operation. 4 | public final class Future: Thenable where Failure : Error { 5 | 6 | @usableFromInline 7 | let lock = Lock() 8 | 9 | @usableFromInline 10 | var _callbacks = CallbackList() 11 | 12 | @usableFromInline 13 | var _isPending: Bool 14 | 15 | @usableFromInline 16 | var _result: Result? 17 | 18 | /// Return true if the future is pending. 19 | @inlinable 20 | public var isPending: Bool { 21 | return self.lock.withLock { self._isPending } 22 | } 23 | 24 | /// Return true if the future is completed. 25 | @inlinable 26 | public var isCompleted: Bool { 27 | return !self.isPending 28 | } 29 | 30 | /// Inspect the future atomically, return nil if the future is pending. 31 | @inlinable 32 | public func inspect() -> Result? { 33 | return self.lock.withLock { self._result } 34 | } 35 | 36 | /// Inspect the future nonatomically, return nil if the future is pending. 37 | @inlinable 38 | public func inspectWithoutLock() -> Result? { 39 | return self._result 40 | } 41 | 42 | @inlinable 43 | init() { 44 | self._isPending = true 45 | } 46 | 47 | @inlinable 48 | public init(result: @autoclosure () -> Result) { 49 | self._result = result() 50 | self._isPending = false 51 | } 52 | 53 | @inlinable 54 | func _complete(_ result: Result) -> CallbackList { 55 | guard self._isPending else { 56 | return CallbackList() 57 | } 58 | 59 | self._result = result 60 | self._isPending = false 61 | 62 | defer { 63 | self._callbacks = CallbackList() 64 | } 65 | return self._callbacks 66 | } 67 | 68 | @inlinable 69 | func complete(_ result: Result) { 70 | self.lock 71 | .withLock { 72 | self._complete(result) 73 | } 74 | .run() 75 | } 76 | 77 | @inlinable 78 | func _whenComplete(_ callback: @escaping () -> CallbackList) -> CallbackList { 79 | guard self._isPending else { 80 | return callback() 81 | } 82 | 83 | self._callbacks.append(callback) 84 | return CallbackList() 85 | } 86 | 87 | /// Add a callback to the future that will be called when the future is completed. 88 | @inlinable 89 | public func whenComplete(_ callback: @escaping (Result) -> Void) { 90 | self.lock 91 | .withLock { 92 | self._whenComplete { [unowned self] in 93 | callback(self._result!) 94 | return CallbackList() 95 | } 96 | } 97 | .run() 98 | } 99 | } 100 | 101 | extension Future { 102 | 103 | @inlinable 104 | public static func success(_ success: Success) -> Future { 105 | return Future(result: .success(success)) 106 | } 107 | 108 | @inlinable 109 | public static func failure(_ failure: Failure) -> Future { 110 | return Future(result: .failure(failure)) 111 | } 112 | } 113 | 114 | @usableFromInline 115 | struct CallbackList { 116 | 117 | @usableFromInline 118 | typealias Element = () -> CallbackList 119 | 120 | @usableFromInline 121 | var first: Element? = nil 122 | 123 | @usableFromInline 124 | var further: [Element]? = nil 125 | 126 | @inlinable 127 | init() { } 128 | 129 | @inlinable 130 | mutating func append(_ callback: @escaping Element) { 131 | if self.first == nil { 132 | self.first = callback 133 | } else { 134 | if self.further == nil { 135 | self.further = [callback] 136 | } else { 137 | self.further!.append(callback) 138 | } 139 | } 140 | } 141 | 142 | @inlinable 143 | func all() -> [Element] { 144 | switch (self.first, self.further) { 145 | case (.none, _): 146 | return [] 147 | case (.some(let cb), .none): 148 | return [cb] 149 | case (.some(let cb), .some(let cbs)): 150 | var arr: [Element] = [] 151 | arr.reserveCapacity(cbs.count + 1) 152 | arr.append(cb) 153 | arr.append(contentsOf: cbs) 154 | return arr 155 | } 156 | } 157 | 158 | @inlinable 159 | func run() { 160 | switch (self.first, self.further) { 161 | case (.none, _): 162 | break 163 | case (.some(let cb), .none): 164 | var cbList = cb() 165 | loop: while true { 166 | switch (cbList.first, cbList.further) { 167 | case (.none, _): 168 | break loop 169 | case (.some(let cb), .none): 170 | cbList = cb() 171 | continue loop 172 | case (.some(let cb), .some(let cbs)): 173 | var next = [cb] + cbs 174 | while next.count > 0 { 175 | let pending = next 176 | next = [] 177 | for cb in pending { 178 | next.append(contentsOf: cb().all()) 179 | } 180 | } 181 | break loop 182 | } 183 | } 184 | case (.some(let cb), .some(let cbs)): 185 | var next = [cb] + cbs 186 | while next.count > 0 { 187 | let pending = next 188 | next = [] 189 | for cb in pending { 190 | next.append(contentsOf: cb().all()) 191 | } 192 | } 193 | } 194 | } 195 | } 196 | 197 | extension CallbackList { 198 | 199 | struct List { 200 | 201 | var storage: [Element] 202 | 203 | init() { 204 | storage = [] 205 | storage.reserveCapacity(4) 206 | } 207 | 208 | init(_ elements: [Element]) { 209 | storage = elements 210 | } 211 | 212 | 213 | mutating func append(_ element: @escaping Element) { 214 | if storage.count == storage.capacity { 215 | storage.reserveCapacity(storage.capacity + 1) 216 | } 217 | 218 | storage.append(element) 219 | } 220 | 221 | mutating func append(_ elements: [Element]) { 222 | if storage.count + elements.count > storage.capacity { 223 | storage.reserveCapacity(storage.count + elements.count) 224 | } 225 | 226 | storage.append(contentsOf: elements) 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /Sources/Future/Helpers/Atom.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @usableFromInline 4 | final class Atom { 5 | 6 | @usableFromInline 7 | let lock = Lock() 8 | 9 | @usableFromInline 10 | var _value: T 11 | 12 | @inlinable 13 | init(_ value: T) { 14 | self._value = value 15 | } 16 | 17 | @inlinable 18 | func get() -> T { 19 | self.lock.lock() 20 | defer { self.lock.unlock() } 21 | return self._value 22 | } 23 | 24 | @inlinable 25 | func set(_ new: T) { 26 | self.lock.lock() 27 | defer { self.lock.unlock() } 28 | self._value = new 29 | } 30 | 31 | @inlinable 32 | func withLock(_ body: (T) throws -> R) rethrows -> R { 33 | self.lock.lock() 34 | defer { self.lock.unlock() } 35 | return try body(self._value) 36 | } 37 | 38 | @inlinable 39 | func withLockMutating(_ body: (inout T) throws -> R) rethrows -> R { 40 | self.lock.lock() 41 | defer { self.lock.unlock() } 42 | return try body(&self._value) 43 | } 44 | } 45 | 46 | extension Atom where T: Equatable { 47 | 48 | @inlinable 49 | func compareAndSet(expected: T, new: T) -> Bool { 50 | return self.withLockMutating { 51 | if $0 == expected { 52 | $0 = new 53 | return true 54 | } 55 | return false 56 | } 57 | } 58 | } 59 | 60 | extension Atom where T: AdditiveArithmetic { 61 | 62 | @inlinable 63 | func add(_ val: T) -> T { 64 | return self.withLockMutating { 65 | let old = $0 66 | $0 += val 67 | return old 68 | } 69 | } 70 | 71 | @inlinable 72 | func sub(_ val: T) -> T { 73 | return self.withLockMutating { 74 | let old = $0 75 | $0 -= val 76 | return old 77 | } 78 | } 79 | } 80 | 81 | extension Atom where T: RangeReplaceableCollection { 82 | 83 | @inlinable 84 | func append(_ new: T.Element) -> T { 85 | return self.withLockMutating { 86 | $0.append(new) 87 | return $0 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/Future/Helpers/Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension NSLocking { 4 | 5 | @inlinable 6 | func withLock(_ body: () throws -> T) rethrows -> T { 7 | self.lock() 8 | defer { self.unlock() } 9 | return try body() 10 | } 11 | 12 | @inlinable 13 | func withLockVoid(_ body: () throws -> Void) rethrows { 14 | self.lock() 15 | defer { self.unlock() } 16 | try body() 17 | } 18 | } 19 | 20 | extension DispatchQueue { 21 | 22 | @inlinable 23 | static func `is`(_ queue: DispatchQueue) -> Bool { 24 | let key = DispatchSpecificKey() 25 | queue.setSpecific(key: key, value: ()) 26 | defer { 27 | queue.setSpecific(key: key, value: nil) 28 | } 29 | return DispatchQueue.getSpecific(key: key) != nil 30 | } 31 | } 32 | 33 | extension Result { 34 | 35 | @inlinable 36 | var value: Success? { 37 | switch self { 38 | case .success(let v): return v 39 | default: return nil 40 | } 41 | } 42 | 43 | @inlinable 44 | var error: Failure? { 45 | switch self { 46 | case .failure(let e): return e 47 | default: return nil 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Future/Helpers/Lock.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | #if canImport(Darwin) 4 | @usableFromInline 5 | final class SpinLock: NSLocking { 6 | 7 | @usableFromInline 8 | var _lock = os_unfair_lock() 9 | 10 | @inlinable 11 | init() { } 12 | 13 | @inlinable 14 | func lock() { 15 | os_unfair_lock_lock(&self._lock) 16 | } 17 | 18 | @inlinable 19 | func unlock() { 20 | os_unfair_lock_unlock(&self._lock) 21 | } 22 | } 23 | #endif 24 | 25 | @usableFromInline 26 | final class Lock: NSLocking { 27 | 28 | @usableFromInline 29 | let wrapped: NSLocking 30 | 31 | @inlinable 32 | init() { 33 | #if canImport(Darwin) 34 | self.wrapped = SpinLock() 35 | #else 36 | self.wrapped = NSLock() 37 | #endif 38 | } 39 | 40 | @inlinable 41 | func lock() { 42 | self.wrapped.lock() 43 | } 44 | 45 | @inlinable 46 | func unlock() { 47 | self.wrapped.unlock() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Future/Promise.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// A promise to provide a result later. 4 | public struct Promise where Failure: Error { 5 | 6 | public let future: Future 7 | 8 | @inlinable 9 | public init() { 10 | self.future = Future() 11 | } 12 | 13 | @inlinable 14 | public func succeed(_ success: Success) { 15 | self.future.complete(.success(success)) 16 | } 17 | 18 | @inlinable 19 | public func fail(_ failure: Failure) { 20 | self.future.complete(.failure(failure)) 21 | } 22 | } 23 | 24 | extension Promise { 25 | 26 | @inlinable 27 | public func complete(_ result: Result) { 28 | self.future.complete(result) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Future/Publisher.swift: -------------------------------------------------------------------------------- 1 | #if canImport(Combine) 2 | import Combine 3 | 4 | @available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 5 | extension Future { 6 | 7 | public func asPublisher() -> Combine.Future { 8 | return .init(self.whenComplete) 9 | } 10 | } 11 | 12 | @available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 13 | extension Publisher { 14 | 15 | public func asFuture(whenNoOutput: @autoclosure @escaping () -> Failure) -> Future { 16 | let p = Promise() 17 | 18 | var cancel: Cancellable? = self.sink(receiveCompletion: { (c) in 19 | switch c { 20 | case .failure(let e): p.fail(e) 21 | case .finished: p.fail(whenNoOutput()) 22 | } 23 | }, receiveValue: { v in 24 | p.succeed(v) 25 | }) 26 | 27 | let future = p.future 28 | 29 | future.whenComplete { _ in 30 | cancel?.cancel() 31 | cancel = nil 32 | } 33 | 34 | return future 35 | } 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /Sources/Future/Scheduler.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Scheduler { 4 | 5 | func schedule(_ task: @escaping () -> Void) 6 | } 7 | 8 | public protocol TimeoutScheduler: Scheduler { 9 | 10 | func schedule(after seconds: Double, _ task: @escaping () -> Void) 11 | } 12 | 13 | extension DispatchQueue: TimeoutScheduler { 14 | 15 | public func schedule(_ task: @escaping () -> Void) { 16 | self.async(execute: task) 17 | } 18 | 19 | public func schedule(after seconds: Double, _ task: @escaping () -> Void) { 20 | self.asyncAfter(wallDeadline: .now() + seconds, execute: task) 21 | } 22 | } 23 | 24 | extension RunLoop: TimeoutScheduler { 25 | 26 | public func schedule(_ task: @escaping () -> Void) { 27 | self.perform(task) 28 | } 29 | 30 | public func schedule(after seconds: Double, _ task: @escaping () -> Void) { 31 | let timer = Timer(timeInterval: seconds, repeats: false) { t in 32 | task() 33 | t.invalidate() 34 | } 35 | self.add(timer, forMode: .common) 36 | } 37 | } 38 | 39 | extension OperationQueue: Scheduler { 40 | 41 | public func schedule(_ task: @escaping () -> Void) { 42 | self.addOperation(task) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Future/Thenable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Thenable { 4 | 5 | associatedtype Success 6 | associatedtype Failure where Failure: Error 7 | 8 | func whenComplete(_ body: @escaping (Result) -> Void) 9 | 10 | func inspect() -> Result? 11 | 12 | func inspectWithoutLock() -> Result? 13 | } 14 | 15 | extension Thenable { 16 | 17 | @inlinable 18 | public func whenSucceed(_ body: @escaping (Success) -> Void) { 19 | self.whenComplete { r in 20 | if case .success(let t) = r { 21 | body(t) 22 | } 23 | } 24 | } 25 | 26 | @inlinable 27 | public func whenFail(_ body: @escaping (Failure) -> Void) { 28 | self.whenComplete { r in 29 | if case .failure(let e) = r { 30 | body(e) 31 | } 32 | } 33 | } 34 | 35 | @inlinable 36 | public func inspectSuccess() -> Success? { 37 | return self.inspect()?.value 38 | } 39 | 40 | @inlinable 41 | public func inspectFailure() -> Failure? { 42 | return self.inspect()?.error 43 | } 44 | } 45 | 46 | extension Thenable { 47 | 48 | @inlinable 49 | public func toFuture() -> Future { 50 | let p = Promise() 51 | self.pipe(to: p) 52 | return p.future 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/FutureTests/AsyncTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | @testable import Future 4 | 5 | class AsyncTests: XCTestCase { 6 | 7 | func testAsync() { 8 | 9 | func task() throws -> Int { 10 | return 1 11 | } 12 | 13 | let e = expectation(description: "testAsync") 14 | 15 | Async 16 | .background { 17 | try task() 18 | } 19 | .main { 20 | XCTAssertEqual($0, 1) 21 | e.fulfill() 22 | } 23 | .mute() 24 | 25 | waitForExpectations(timeout: 0.1, handler: nil) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/FutureTests/FeaturesTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | @testable import Future 4 | 5 | class FeaturesTests: XCTestCase { 6 | 7 | func testAlways() { 8 | var i = 0 9 | 10 | let p1 = Promise() 11 | p1.future 12 | .always { 13 | i += 1 14 | } 15 | .hush() 16 | 17 | let p2 = Promise() 18 | p2.future 19 | .always { 20 | i += 1 21 | } 22 | .hush() 23 | 24 | p1.succeed(true) 25 | p2.fail(TestError.e1) 26 | 27 | XCTAssertEqual(i, 2) 28 | } 29 | 30 | func testAnd() { 31 | var i = 0 32 | 33 | let p1 = Promise() 34 | let p2 = Promise() 35 | 36 | var r: (Int, Int)? 37 | p1.future.and(p2.future).whenSucceed { 38 | i += 1 39 | r = $0 40 | } 41 | 42 | p1.succeed(1) 43 | XCTAssertEqual(i, 0) 44 | XCTAssertNil(r) 45 | 46 | p2.succeed(2) 47 | XCTAssertEqual(i, 1) 48 | XCTAssertNotNil(r) 49 | 50 | XCTAssertEqual(r?.0, 1) 51 | XCTAssertEqual(r?.1, 2) 52 | 53 | var r1: (Int, Int)? 54 | p1.future.and(3).whenSucceed { 55 | r1 = $0 56 | } 57 | 58 | XCTAssertEqual(r1?.0, 1) 59 | XCTAssertEqual(r1?.1, 3) 60 | } 61 | 62 | func testAsAny() { 63 | let p = Promise() 64 | 65 | var i: Any? 66 | p.future.asAny().whenSucceed { 67 | i = $0 68 | } 69 | 70 | p.succeed(1) 71 | 72 | XCTAssertEqual((i as? Int), 1) 73 | } 74 | 75 | func testAsVoid() { 76 | let p = Promise() 77 | 78 | var i = 0 79 | p.future.asVoid().whenSucceed { 80 | i += 1 81 | } 82 | 83 | p.succeed(1) 84 | 85 | XCTAssertEqual(i, 1) 86 | } 87 | 88 | func testCatch() { 89 | let p = Promise() 90 | 91 | var e: TestError? 92 | p.future 93 | .catch { 94 | e = $0 95 | } 96 | .hush() 97 | 98 | p.fail(TestError.e1) 99 | 100 | XCTAssertNotNil(e) 101 | XCTAssertTrue(e! == TestError.e1) 102 | } 103 | 104 | func testDelay() { 105 | let e = expectation(description: "testDelay") 106 | 107 | let p = Promise() 108 | 109 | var ed: Date? 110 | p.future.delay(0.1, on: .main).whenSucceed { _ in 111 | ed = Date() 112 | XCTAssertTrue(Thread.isMainThread) 113 | e.fulfill() 114 | } 115 | 116 | let sd = Date() 117 | p.succeed(1) 118 | 119 | waitForExpectations(timeout: 0.2, handler: nil) 120 | 121 | XCTAssertNotNil(ed) 122 | XCTAssertGreaterThan(ed!.timeIntervalSince(sd), 0.1) 123 | } 124 | 125 | func testDone() { 126 | let p = Promise() 127 | 128 | var i: Int? 129 | p.future.done { 130 | i = $0 131 | } 132 | .mute() 133 | 134 | p.succeed(1) 135 | 136 | XCTAssertEqual(i, 1) 137 | } 138 | 139 | func testFlat() { 140 | let f = Future.success(1).map { Future.success($0) }.flat() 141 | XCTAssertEqual(f.inspectSuccess(), 1) 142 | } 143 | 144 | func testFlatMap() { 145 | let f1 = Future.success(1).flatMap { _ in Future.success(true) } 146 | XCTAssertEqual(f1.inspectSuccess(), true) 147 | 148 | enum E: Error { 149 | case e 150 | } 151 | 152 | let f2 = Future.failure(E.e).flatMapError { _ in Future.failure(TestError.e1) } 153 | XCTAssertNotNil(f2.inspectFailure()) 154 | XCTAssertTrue(f2.inspectFailure()! == TestError.e1) 155 | } 156 | 157 | func testMap() { 158 | let f1 = Future.success(true).map { _ in 1 } 159 | XCTAssertEqual(f1.inspectSuccess(), 1) 160 | 161 | enum E: Error { 162 | case e 163 | } 164 | let f2 = Future.failure(E.e).mapError { _ in TestError.e1 } 165 | XCTAssertNotNil(f2.inspectFailure()) 166 | XCTAssertTrue(f2.inspectFailure()! == TestError.e1) 167 | } 168 | 169 | func testPipe() { 170 | let p1 = Promise() 171 | let p2 = Promise() 172 | 173 | var i = 0 174 | p1.future.whenSucceed { 175 | i = $0 176 | } 177 | p2.future.whenFail { _ in 178 | i += 1 179 | } 180 | 181 | Future.success(1).pipeSuccess(to: p1) 182 | XCTAssertEqual(i, 1) 183 | 184 | Future.failure(TestError.e1).pipeFailure(to: p2) 185 | XCTAssertEqual(i, 2) 186 | } 187 | 188 | func testRecover() { 189 | let f = Future.failure(TestError.e1).recover { _ in 1 } 190 | XCTAssertEqual(f.wait()!, 1) 191 | } 192 | 193 | func testReduce() { 194 | let futures = (1...9).map { Future.success($0) } 195 | 196 | let final = Async.reduce(futures, initial: 0) { (x, y) -> Future in 197 | Future.success(x + y) 198 | } 199 | 200 | var i = 0 201 | final.whenSucceed { 202 | i = $0 203 | } 204 | XCTAssertEqual(i, 45) 205 | } 206 | 207 | func testRetry() { 208 | var count = 3 209 | 210 | let f = Async.retry(count: 3) { () -> Future in 211 | let p = Promise() 212 | 213 | if count > 0 { 214 | p.fail(TestError.e1) 215 | count -= 1 216 | } else { 217 | p.succeed(1) 218 | } 219 | 220 | return p.future 221 | } 222 | 223 | var i = 0 224 | f.whenSucceed { 225 | i = $0 226 | } 227 | 228 | XCTAssertEqual(i, 1) 229 | } 230 | 231 | func testSome() { 232 | let p1 = Promise() 233 | let p2 = Promise() 234 | let p3 = Promise() 235 | 236 | Async.some([p1.future, p2.future, p3.future], count: 2).whenSucceed { (rs) in 237 | XCTAssertEqual(rs, [1, 3]) 238 | } 239 | 240 | p1.succeed(1) 241 | p3.succeed(3) 242 | p2.succeed(2) 243 | } 244 | 245 | func testTap() { 246 | Future.success(1).tap { 247 | XCTAssertEqual($0, 1) 248 | }.mute() 249 | 250 | Future.failure(TestError.e1).tapError { 251 | XCTAssertTrue($0 == TestError.e1) 252 | }.mute() 253 | } 254 | 255 | func testTimeout() { 256 | let p = Promise() 257 | DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) { 258 | p.succeed(1) 259 | } 260 | let timeout = p.future.timeout(0.1, on: .main, whenTimeout: .e1) 261 | 262 | let e = expectation(description: "testTimeout") 263 | timeout.whenComplete { _ in 264 | print("whenComplete") 265 | e.fulfill() 266 | } 267 | 268 | waitForExpectations(timeout: 0.2, handler: nil) 269 | XCTAssertNotNil(timeout.inspectFailure()) 270 | XCTAssertTrue(timeout.inspectFailure()! == .e1) 271 | } 272 | 273 | func testValidate() { 274 | let e = Future.success(1) 275 | .validate({ $0 % 2 == 0 }, whenFail: .e1) 276 | .waitError() 277 | 278 | XCTAssertNotNil(e) 279 | XCTAssertTrue(e! == .e1) 280 | } 281 | 282 | 283 | func testWhenAll() { 284 | let p1 = Promise() 285 | let p2 = Promise() 286 | let p3 = Promise() 287 | 288 | var i = 0 289 | p1.future.whenComplete { _ in i += 1 } 290 | p2.future.whenComplete { _ in i += 1 } 291 | p3.future.whenComplete { _ in i += 1 } 292 | 293 | Async.whenAllCompleteVoid([p1.future, p2.future, p3.future]).whenSucceed { _ in 294 | i += 1 295 | } 296 | 297 | Async.whenAllSucceed([p1.future, p2.future, p3.future]).whenSucceed { r in 298 | i += 1 299 | XCTAssertEqual(r, [1, 2, 3]) 300 | } 301 | 302 | p1.succeed(1) 303 | p2.succeed(2) 304 | p3.succeed(3) 305 | 306 | XCTAssertEqual(i, 5) 307 | } 308 | 309 | func testWhenAny() { 310 | let p1 = Promise() 311 | let p2 = Promise() 312 | let p3 = Promise() 313 | 314 | var i = 0 315 | Async.whenAnyComplete(p1.future, p2.future, p3.future) 316 | .whenSucceed { 317 | i = $0 318 | } 319 | 320 | p2.succeed(2) 321 | 322 | XCTAssertEqual(i, 2) 323 | } 324 | 325 | func testYield() { 326 | let p1 = Promise() 327 | let q1 = DispatchQueue(label: UUID().uuidString) 328 | let e1 = expectation(description: "testYieldDispatchQueue") 329 | p1.future.yield(on: q1).whenSucceed { _ in 330 | XCTAssertTrue(DispatchQueue.`is`(q1)) 331 | e1.fulfill() 332 | } 333 | 334 | DispatchQueue.global().async { 335 | p1.succeed(1) 336 | } 337 | 338 | waitForExpectations(timeout: 0.5, handler: nil) 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /Tests/FutureTests/FutureTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Future 3 | 4 | class FutureTests: XCTestCase { 5 | 6 | func testPending() { 7 | let p = Promise() 8 | 9 | let f = p.future 10 | 11 | XCTAssertTrue(f.isPending) 12 | XCTAssertFalse(f.isCompleted) 13 | 14 | p.succeed(true) 15 | XCTAssertTrue(f.isCompleted) 16 | XCTAssertFalse(f.isPending) 17 | 18 | p.fail(TestError.e1) 19 | XCTAssertTrue(f.isCompleted) 20 | XCTAssertFalse(f.isPending) 21 | } 22 | 23 | func testInspect() { 24 | let p = Promise() 25 | let f = p.future 26 | 27 | XCTAssertNil(f.inspect()) 28 | XCTAssertNil(f.inspectWithoutLock()) 29 | 30 | p.succeed(true) 31 | XCTAssertNotNil(f.inspect()) 32 | XCTAssertNotNil(f.inspectWithoutLock()) 33 | 34 | XCTAssertEqual(f.inspect()?.value, true) 35 | 36 | let failedFuture = Future.failure(TestError.e1) 37 | XCTAssertNotNil(failedFuture.inspect()) 38 | XCTAssertTrue(failedFuture.inspect()!.error == TestError.e1) 39 | } 40 | 41 | func testComplete() { 42 | var count = 0 43 | let p1 = Promise() 44 | 45 | p1.future.whenSucceed { _ in 46 | count += 1 47 | } 48 | p1.succeed(true) 49 | 50 | XCTAssertEqual(count, 1) 51 | 52 | let p2 = Promise() 53 | p2.future.whenFail { _ in 54 | count += 1 55 | } 56 | p2.fail(TestError.e1) 57 | 58 | XCTAssertEqual(count, 2) 59 | } 60 | 61 | func testMoreObservers() { 62 | var count = 0 63 | 64 | let p = Promise() 65 | p.future.whenSucceed { _ in 66 | count += 1 67 | } 68 | p.future.whenSucceed { _ in 69 | count += 1 70 | } 71 | p.future.whenSucceed { _ in 72 | count += 1 73 | } 74 | 75 | p.succeed(true) 76 | 77 | XCTAssertEqual(count, 3) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Tests/FutureTests/Helpers.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum TestError: Error { 4 | case e1 5 | case e2 6 | } 7 | 8 | -------------------------------------------------------------------------------- /Tests/FutureTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(FutureTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import FutureTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += FutureTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /benchmark/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "BrightFutures", 6 | "repositoryURL": "https://github.com/Thomvis/BrightFutures", 7 | "state": { 8 | "branch": null, 9 | "revision": "9279defa6bdc21501ce740266e5a14d0119ddc63", 10 | "version": "8.0.1" 11 | } 12 | }, 13 | { 14 | "package": "Hydra", 15 | "repositoryURL": "https://github.com/malcommac/Hydra.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "413421fc8a081ee00eb1029f36703f3474c4a3f0", 19 | "version": "2.0.0" 20 | } 21 | }, 22 | { 23 | "package": "Nimble", 24 | "repositoryURL": "https://github.com/Quick/Nimble.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "43304bf2b1579fd555f2fdd51742771c1e4f2b98", 28 | "version": "8.0.1" 29 | } 30 | }, 31 | { 32 | "package": "PromiseKit", 33 | "repositoryURL": "https://github.com/mxcl/PromiseKit", 34 | "state": { 35 | "branch": null, 36 | "revision": "fe1e9c5b62465227cceb7b0e6e79489ba7b824af", 37 | "version": "6.8.4" 38 | } 39 | }, 40 | { 41 | "package": "Promises", 42 | "repositoryURL": "https://github.com/google/promises.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "6b6f4fe3385ed37389fb231bc40be81de2f1230f", 46 | "version": "1.2.8" 47 | } 48 | }, 49 | { 50 | "package": "Quick", 51 | "repositoryURL": "https://github.com/Quick/Quick.git", 52 | "state": { 53 | "branch": null, 54 | "revision": "94df9b449508344667e5afc7e80f8bcbff1e4c37", 55 | "version": "2.1.0" 56 | } 57 | }, 58 | { 59 | "package": "ReactiveSwift", 60 | "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift.git", 61 | "state": { 62 | "branch": null, 63 | "revision": "c37950dc5020544c58d4bf47c0d028893686b9e6", 64 | "version": "5.0.1" 65 | } 66 | }, 67 | { 68 | "package": "Result", 69 | "repositoryURL": "https://github.com/antitypical/Result.git", 70 | "state": { 71 | "branch": null, 72 | "revision": "2ca499ba456795616fbc471561ff1d963e6ae160", 73 | "version": "4.1.0" 74 | } 75 | }, 76 | { 77 | "package": "RxSwift", 78 | "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", 79 | "state": { 80 | "branch": null, 81 | "revision": "b3e888b4972d9bc76495dd74d30a8c7fad4b9395", 82 | "version": "5.0.1" 83 | } 84 | } 85 | ] 86 | }, 87 | "version": 1 88 | } 89 | -------------------------------------------------------------------------------- /benchmark/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "benchmark", 7 | platforms: [ 8 | .macOS(.v10_12), 9 | .iOS(.v10), 10 | .tvOS(.v10), 11 | .watchOS(.v3) 12 | ], 13 | dependencies: [ 14 | .package(url: "https://github.com/mxcl/PromiseKit", from: "6.8.0"), 15 | .package(url: "https://github.com/Thomvis/BrightFutures", from: "8.0.0"), 16 | .package(url: "https://github.com/malcommac/Hydra.git", from: "2.0.0"), 17 | .package(url: "https://github.com/google/promises.git", from: "1.2.8"), 18 | .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "5.0.0"), 19 | .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift.git", from: "5.0.0"), 20 | .package(path: "../") 21 | ], 22 | targets: [ 23 | .target( 24 | name: "benchmark", 25 | dependencies: [ 26 | "PromiseKit", 27 | "BrightFutures", 28 | "Hydra", 29 | "Promises", 30 | "RxSwift", 31 | "ReactiveSwift", 32 | "Future" 33 | ]), 34 | .testTarget( 35 | name: "benchmarkTests", 36 | dependencies: ["benchmark"]), 37 | ] 38 | ) 39 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | # benchmark 2 | 3 | A description of this package. 4 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/main.swift: -------------------------------------------------------------------------------- 1 | import RxSwift 2 | 3 | let tests: [Test] = [ 4 | TestDispatch(), 5 | TestFuture(), 6 | TestPromises(), 7 | TestPromiseKit(), 8 | TestBrightFutures(), 9 | TestHydra(), 10 | TestRxSwift(), 11 | TestReactive() 12 | ] 13 | 14 | func testSerial() { 15 | 16 | for test in tests { 17 | test.testSerialQueue() 18 | test.testDoubleSerialQueue() 19 | test.testTripleSerialQueue() 20 | } 21 | } 22 | 23 | func testConcurrent() { 24 | 25 | for test in tests { 26 | test.testConcurrentQueue() 27 | } 28 | } 29 | 30 | testSerial() 31 | testConcurrent() 32 | 33 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/src/BrightFutures.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BrightFutures.swift 3 | // benchmark 4 | // 5 | // Created by Quentin Jin on 2019/6/2. 6 | // Copyright © 2019 Quentin MED. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import BrightFutures 11 | 12 | class TestBrightFutures: Test { 13 | 14 | func executor(_ q: DispatchQueue) -> ExecutionContext { 15 | return { task in 16 | q.async { 17 | task() 18 | } 19 | } 20 | } 21 | 22 | func testSerialQueue() { 23 | 24 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 25 | let s = DispatchSemaphore(value: 0) 26 | 27 | let exe = executor(q) 28 | 29 | let time = benchmark(TIMES) { 30 | Future.init(value: true) 31 | .onSuccess(exe) { _ in 32 | s.signal() 33 | } 34 | s.wait() 35 | } 36 | 37 | Log("BrightFutures serial queue", time) 38 | } 39 | 40 | func testDoubleSerialQueue() { 41 | 42 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 43 | let s = DispatchSemaphore(value: 0) 44 | 45 | let exe = executor(q) 46 | 47 | let time = benchmark(TIMES) { 48 | Future.init(value: true) 49 | .map(exe) { 50 | $0 51 | } 52 | .onSuccess(exe) { _ in 53 | s.signal() 54 | } 55 | 56 | s.wait() 57 | } 58 | 59 | Log("BrightFutures double serial queue", time) 60 | } 61 | 62 | func testTripleSerialQueue() { 63 | 64 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 65 | let s = DispatchSemaphore(value: 0) 66 | 67 | let exe = executor(q) 68 | 69 | let time = benchmark(TIMES) { 70 | Future.init(value: true) 71 | .map(exe) { 72 | $0 73 | } 74 | .map(exe) { 75 | $0 76 | } 77 | .onSuccess(exe) { _ in 78 | s.signal() 79 | } 80 | 81 | s.wait() 82 | } 83 | 84 | Log("BrightFutures triple serial queue", time) 85 | } 86 | 87 | func testConcurrentQueue() { 88 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated, attributes: .concurrent) 89 | let g = DispatchGroup() 90 | 91 | let exe = executor(q) 92 | 93 | var promises: [Promise] = [] 94 | 95 | for _ in 0..() 98 | promise.future.onSuccess(exe) { _ in 99 | g.leave() 100 | } 101 | promises.append(promise) 102 | } 103 | 104 | let time = benchmark(1) { 105 | for promise in promises { 106 | promise.success(true) 107 | } 108 | 109 | g.wait() 110 | } 111 | 112 | Log("BrightFutures concurrent queue", time / TIMES) 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/src/Dispatch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dispatch.swift 3 | // benchmark 4 | // 5 | // Created by Quentin Jin on 2019/5/31. 6 | // Copyright © 2019 Quentin Jin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TestDispatch: Test { 12 | 13 | func testSerialQueue() { 14 | 15 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 16 | let s = DispatchSemaphore(value: 0) 17 | 18 | let time = benchmark(TIMES) { 19 | q.async { 20 | s.signal() 21 | } 22 | s.wait() 23 | } 24 | 25 | Log("dispatch serial queue", time) 26 | } 27 | 28 | func testDoubleSerialQueue() { 29 | 30 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 31 | let s = DispatchSemaphore(value: 0) 32 | 33 | let time = benchmark(TIMES) { 34 | q.async { 35 | q.async { 36 | s.signal() 37 | } 38 | } 39 | s.wait() 40 | } 41 | 42 | Log("dispatch double serial queue", time) 43 | } 44 | 45 | func testTripleSerialQueue() { 46 | 47 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 48 | let s = DispatchSemaphore(value: 0) 49 | 50 | let time = benchmark(TIMES) { 51 | q.async { 52 | q.async { 53 | q.async { 54 | s.signal() 55 | } 56 | } 57 | } 58 | s.wait() 59 | } 60 | 61 | Log("dispatch triple serial queue", time) 62 | } 63 | 64 | func testConcurrentQueue() { 65 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated, attributes: .concurrent) 66 | let g = DispatchGroup() 67 | 68 | var bodys: [() -> Void] = [] 69 | 70 | for _ in 0...success(true) 21 | .yield(on: q) 22 | .whenSuccess { _ in 23 | s.signal() 24 | } 25 | 26 | s.wait() 27 | } 28 | 29 | Log("futureQ serial queue", time) 30 | 31 | } 32 | 33 | func testDoubleSerialQueue() { 34 | 35 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 36 | let s = DispatchSemaphore(value: 0) 37 | 38 | let time = benchmark(TIMES) { 39 | FutureE.success(true) 40 | .yield(on: q) 41 | .yield(on: q) 42 | .whenSuccess { _ in 43 | s.signal() 44 | } 45 | 46 | s.wait() 47 | } 48 | 49 | Log("futureQ double serial queue", time) 50 | } 51 | 52 | func testTripleSerialQueue() { 53 | 54 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 55 | let s = DispatchSemaphore(value: 0) 56 | 57 | let time = benchmark(TIMES) { 58 | FutureE.success(true) 59 | .yield(on: q) 60 | .yield(on: q) 61 | .whenSuccess { _ in 62 | s.signal() 63 | } 64 | 65 | s.wait() 66 | } 67 | 68 | Log("futureQ triple serial queue", time) 69 | } 70 | 71 | func testConcurrentQueue() { 72 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated, attributes: .concurrent) 73 | let g = DispatchGroup() 74 | 75 | var promises: [PromiseE] = [] 76 | 77 | for _ in 0..() 80 | promise.future.whenSuccess { _ in 81 | g.leave() 82 | } 83 | promises.append(promise) 84 | } 85 | 86 | let time = benchmark(1) { 87 | for promise in promises { 88 | q.async { 89 | promise.succeed(true) 90 | } 91 | } 92 | 93 | g.wait() 94 | } 95 | 96 | Log("futureQ concurrent queue", time / TIMES) 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/src/Helpers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Helpers.swift 3 | // benchmark 4 | // 5 | // Created by Quentin MED on 2019/5/31. 6 | // Copyright © 2019 Quentin MED. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Test { 12 | 13 | func testSerialQueue() 14 | 15 | func testDoubleSerialQueue() 16 | 17 | func testTripleSerialQueue() 18 | 19 | func testConcurrentQueue() 20 | } 21 | 22 | func benchmark(_ times: UInt64, _ body: () -> Void) -> UInt64 { 23 | let timestamp = DispatchTime.now().uptimeNanoseconds 24 | for _ in 0..(resolved: true) 23 | .then(in: ctx) { _ in 24 | s.signal() 25 | } 26 | s.wait() 27 | } 28 | 29 | Log("hydra serial queue", time) 30 | } 31 | 32 | func testDoubleSerialQueue() { 33 | 34 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 35 | let s = DispatchSemaphore(value: 0) 36 | 37 | let ctx = Context.custom(queue: q) 38 | 39 | let time = benchmark(TIMES) { 40 | Promise.init(resolved: true) 41 | .then(in: ctx) { 42 | $0 43 | } 44 | .then(in: ctx) { _ in 45 | s.signal() 46 | } 47 | 48 | s.wait() 49 | } 50 | 51 | Log("hydra double serial queue", time) 52 | } 53 | 54 | func testTripleSerialQueue() { 55 | 56 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 57 | let s = DispatchSemaphore(value: 0) 58 | 59 | let ctx = Context.custom(queue: q) 60 | 61 | let time = benchmark(TIMES) { 62 | Promise.init(resolved: true) 63 | .then(in: ctx) { 64 | $0 65 | } 66 | .then(in: ctx) { 67 | $0 68 | } 69 | .then(in: ctx) { _ in 70 | s.signal() 71 | } 72 | 73 | s.wait() 74 | } 75 | 76 | Log("hydra triple serial queue", time) 77 | } 78 | 79 | func testConcurrentQueue() { 80 | 81 | Log("hydra concurrent queue(N/A)", 0) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/src/PromiseKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PromiseKit.swift 3 | // benchmark 4 | // 5 | // Created by Quentin Jin on 2019/5/31. 6 | // Copyright © 2019 Quentin Jin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PromiseKit 11 | 12 | class TestPromiseKit: Test { 13 | 14 | func testSerialQueue() { 15 | 16 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 17 | let s = DispatchSemaphore(value: 0) 18 | 19 | let time = benchmark(TIMES) { 20 | Promise.value(true) 21 | .done(on: q) { _ in 22 | s.signal() 23 | } 24 | s.wait() 25 | } 26 | 27 | Log("promisekit serial queue", time) 28 | } 29 | 30 | func testDoubleSerialQueue() { 31 | 32 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 33 | let s = DispatchSemaphore(value: 0) 34 | 35 | let time = benchmark(TIMES) { 36 | Promise.value(true) 37 | .map(on: q) { 38 | $0 39 | } 40 | .done(on: q) { _ in 41 | s.signal() 42 | } 43 | 44 | s.wait() 45 | } 46 | 47 | Log("promisekit double serial queue", time) 48 | } 49 | 50 | func testTripleSerialQueue() { 51 | 52 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 53 | let s = DispatchSemaphore(value: 0) 54 | 55 | let time = benchmark(TIMES) { 56 | Promise.value(true) 57 | .map(on: q) { 58 | $0 59 | } 60 | .map(on: q) { 61 | $0 62 | } 63 | .done(on: q) { _ in 64 | s.signal() 65 | } 66 | 67 | s.wait() 68 | } 69 | 70 | Log("promisekit triple serial queue", time) 71 | } 72 | 73 | func testConcurrentQueue() { 74 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated, attributes: .concurrent) 75 | let g = DispatchGroup() 76 | 77 | var pendings: [(Promise, Resolver)] = [] 78 | 79 | for _ in 0...pending() 82 | pending.0.done(on: q) { _ in 83 | g.leave() 84 | } 85 | pendings.append(pending) 86 | } 87 | 88 | let time = benchmark(1) { 89 | for pending in pendings { 90 | pending.1.fulfill(true) 91 | } 92 | 93 | g.wait() 94 | } 95 | 96 | Log("promisekit concurrent queue", time / TIMES) 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/src/Promises.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Promises.swift 3 | // benchmark 4 | // 5 | // Created by Quentin Jin on 2019/5/31. 6 | // Copyright © 2019 Quentin Jin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Promises 11 | 12 | class TestPromises: Test { 13 | 14 | func testSerialQueue() { 15 | 16 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 17 | let s = DispatchSemaphore(value: 0) 18 | 19 | let time = benchmark(TIMES) { 20 | Promise(true).then(on: q) { _ in 21 | s.signal() 22 | } 23 | s.wait() 24 | } 25 | 26 | Log("promises serial queue", time) 27 | } 28 | 29 | func testDoubleSerialQueue() { 30 | 31 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 32 | let s = DispatchSemaphore(value: 0) 33 | 34 | let time = benchmark(TIMES) { 35 | Promise(true) 36 | .then(on: q) { _ in 37 | } 38 | .then(on: q) { _ in 39 | s.signal() 40 | } 41 | 42 | s.wait() 43 | } 44 | 45 | Log("promises double serial queue", time) 46 | } 47 | 48 | func testTripleSerialQueue() { 49 | 50 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 51 | let s = DispatchSemaphore(value: 0) 52 | 53 | let time = benchmark(TIMES) { 54 | Promise(true) 55 | .then(on: q) { _ in 56 | } 57 | .then(on: q) { _ in 58 | } 59 | .then(on: q) { _ in 60 | s.signal() 61 | } 62 | 63 | s.wait() 64 | } 65 | 66 | Log("promises triple serial queue", time) 67 | } 68 | 69 | func testConcurrentQueue() { 70 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated, attributes: .concurrent) 71 | let g = DispatchGroup() 72 | 73 | var promises: [Promise] = [] 74 | 75 | for _ in 0...pending() 78 | promise.then(on: q) { _ in 79 | g.leave() 80 | } 81 | promises.append(promise) 82 | } 83 | 84 | let time = benchmark(1) { 85 | for promise in promises { 86 | promise.fulfill(true) 87 | } 88 | 89 | g.wait() 90 | } 91 | 92 | Log("promises concurrent queue", time / TIMES) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/src/Reactive.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reactive.swift 3 | // benchmark 4 | // 5 | // Created by Quentin Jin on 2019/6/2. 6 | // Copyright © 2019 Quentin MED. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ReactiveSwift 11 | 12 | class TestReactive: Test { 13 | 14 | func testSerialQueue() { 15 | 16 | let s = DispatchSemaphore(value: 0) 17 | 18 | let scheduler = QueueScheduler(qos: .userInitiated, name: "", targeting: nil) 19 | 20 | let time = benchmark(TIMES) { 21 | SignalProducer.init(value: true) 22 | .observe(on: scheduler) 23 | .startWithValues({ (_) in 24 | s.signal() 25 | }) 26 | s.wait() 27 | } 28 | 29 | Log("reactive serial queue", time) 30 | 31 | } 32 | 33 | func testDoubleSerialQueue() { 34 | 35 | let s = DispatchSemaphore(value: 0) 36 | 37 | let scheduler = QueueScheduler(qos: .userInitiated, name: "", targeting: nil) 38 | 39 | let time = benchmark(TIMES) { 40 | SignalProducer.init(value: true) 41 | .observe(on: scheduler) 42 | .observe(on: scheduler) 43 | .startWithValues({ (_) in 44 | s.signal() 45 | }) 46 | 47 | s.wait() 48 | } 49 | 50 | Log("reactive double serial queue", time) 51 | } 52 | 53 | func testTripleSerialQueue() { 54 | 55 | let s = DispatchSemaphore(value: 0) 56 | 57 | let scheduler = QueueScheduler(qos: .userInitiated, name: "", targeting: nil) 58 | 59 | let time = benchmark(TIMES) { 60 | SignalProducer.init(value: true) 61 | .observe(on: scheduler) 62 | .observe(on: scheduler) 63 | .observe(on: scheduler) 64 | .startWithValues({ (_) in 65 | s.signal() 66 | }) 67 | 68 | s.wait() 69 | } 70 | 71 | Log("reactive triple serial queue", time) 72 | } 73 | 74 | class ConcurrentQueueScheduler: Scheduler { 75 | 76 | let q: DispatchQueue 77 | init(_ q: DispatchQueue) { 78 | self.q = q 79 | } 80 | 81 | @discardableResult 82 | public func schedule(_ action: @escaping () -> Void) -> Disposable? { 83 | let d = AnyDisposable() 84 | 85 | q.async { 86 | if !d.isDisposed { 87 | action() 88 | } 89 | } 90 | 91 | return d 92 | } 93 | } 94 | 95 | func testConcurrentQueue() { 96 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated, attributes: .concurrent) 97 | let g = DispatchGroup() 98 | 99 | let scheduler = ConcurrentQueueScheduler(q) 100 | 101 | var observers: [Signal.Observer] = [] 102 | 103 | for _ in 0...pipe() 107 | pipe.output.observe(on: scheduler).observeResult { (_) in 108 | g.leave() 109 | } 110 | observers.append(pipe.input) 111 | } 112 | 113 | let time = benchmark(1) { 114 | for observer in observers { 115 | observer.send(value: true) 116 | } 117 | 118 | g.wait() 119 | } 120 | 121 | Log("reactive concurrent queue", time / TIMES) 122 | } 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /benchmark/Sources/benchmark/src/RxSwift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxSwift.swift 3 | // benchmark 4 | // 5 | // Created by Quentin Jin on 2019/6/2. 6 | // Copyright © 2019 Quentin MED. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | 12 | class TestRxSwift: Test { 13 | 14 | let bag = DisposeBag() 15 | 16 | func testSerialQueue() { 17 | 18 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 19 | let s = DispatchSemaphore(value: 0) 20 | 21 | let scheduler = SerialDispatchQueueScheduler(queue: q, internalSerialQueueName: UUID().uuidString) 22 | 23 | let time = benchmark(TIMES) { 24 | Observable.just(true) 25 | .observeOn(scheduler) 26 | .subscribe(onNext: { (_) in 27 | s.signal() 28 | }) 29 | .disposed(by: bag) 30 | s.wait() 31 | } 32 | 33 | Log("rxswift serial queue", time) 34 | 35 | } 36 | 37 | func testDoubleSerialQueue() { 38 | 39 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 40 | let s = DispatchSemaphore(value: 0) 41 | 42 | let scheduler = SerialDispatchQueueScheduler(queue: q, internalSerialQueueName: UUID().uuidString) 43 | 44 | let time = benchmark(TIMES) { 45 | Observable.just(true) 46 | .observeOn(scheduler) 47 | .observeOn(scheduler) 48 | .subscribe(onNext: { (_) in 49 | s.signal() 50 | }) 51 | .disposed(by: bag) 52 | 53 | s.wait() 54 | } 55 | 56 | Log("rxswift double serial queue", time) 57 | } 58 | 59 | func testTripleSerialQueue() { 60 | 61 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated) 62 | let s = DispatchSemaphore(value: 0) 63 | 64 | let scheduler = SerialDispatchQueueScheduler(queue: q, internalSerialQueueName: UUID().uuidString) 65 | 66 | let time = benchmark(TIMES) { 67 | Observable.just(true) 68 | .observeOn(scheduler) 69 | .observeOn(scheduler) 70 | .observeOn(scheduler) 71 | .subscribe(onNext: { (_) in 72 | s.signal() 73 | }) 74 | .disposed(by: bag) 75 | 76 | s.wait() 77 | } 78 | 79 | Log("rxswift triple serial queue", time) 80 | } 81 | 82 | func testConcurrentQueue() { 83 | let q = DispatchQueue(label: UUID().uuidString, qos: .userInitiated, attributes: .concurrent) 84 | let g = DispatchGroup() 85 | 86 | var subjects: [PublishSubject] = [] 87 | 88 | let scheduler = SerialDispatchQueueScheduler(queue: q, internalSerialQueueName: UUID().uuidString) 89 | 90 | for _ in 0..() 94 | subject 95 | .observeOn(scheduler) 96 | .subscribe(onNext: { (_) in 97 | g.leave() 98 | }) 99 | .disposed(by: bag) 100 | 101 | subjects.append(subject) 102 | } 103 | 104 | let time = benchmark(1) { 105 | for subject in subjects { 106 | subject.onNext(true) 107 | } 108 | 109 | g.wait() 110 | } 111 | 112 | Log("rxswift concurrent queue", time / TIMES) 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /benchmark/Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import benchmarkTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += benchmarkTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /benchmark/Tests/benchmarkTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(benchmarkTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /benchmark/Tests/benchmarkTests/benchmarkTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import class Foundation.Bundle 3 | 4 | final class benchmarkTests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | 10 | // Some of the APIs that we use below are available in macOS 10.13 and above. 11 | guard #available(macOS 10.13, *) else { 12 | return 13 | } 14 | 15 | let fooBinary = productsDirectory.appendingPathComponent("benchmark") 16 | 17 | let process = Process() 18 | process.executableURL = fooBinary 19 | 20 | let pipe = Pipe() 21 | process.standardOutput = pipe 22 | 23 | try process.run() 24 | process.waitUntilExit() 25 | 26 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 27 | let output = String(data: data, encoding: .utf8) 28 | 29 | XCTAssertEqual(output, "Hello, world!\n") 30 | } 31 | 32 | /// Returns path to the built products directory. 33 | var productsDirectory: URL { 34 | #if os(macOS) 35 | for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { 36 | return bundle.bundleURL.deletingLastPathComponent() 37 | } 38 | fatalError("couldn't find the products directory") 39 | #else 40 | return Bundle.main.bundleURL 41 | #endif 42 | } 43 | 44 | static var allTests = [ 45 | ("testExample", testExample), 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/BrightFutures_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/FBLPromises_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/FutureQ_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/Future_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/Hydra_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/PromiseKit_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/Promises_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/ReactiveSwift_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/Result_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/RxSwift_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/benchmarkTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/xcshareddata/xcschemes/benchmark-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /benchmark/benchmark.xcodeproj/xcshareddata/xcschemes/benchmark.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luoxiu/Future/3f6066ceec21e56a551a6c59fc0449b5f51d7c79/logo.png -------------------------------------------------------------------------------- /resources/benchmark-concurrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luoxiu/Future/3f6066ceec21e56a551a6c59fc0449b5f51d7c79/resources/benchmark-concurrent.png -------------------------------------------------------------------------------- /resources/benchmark-serial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luoxiu/Future/3f6066ceec21e56a551a6c59fc0449b5f51d7c79/resources/benchmark-serial.png --------------------------------------------------------------------------------