tomlokhorst / promissum Goto Github PK
View Code? Open in Web Editor NEWA promises library written in Swift featuring combinators like map, flatMap, whenAll, whenAny.
License: MIT License
A promises library written in Swift featuring combinators like map, flatMap, whenAll, whenAny.
License: MIT License
When importing https://github.com/ReactiveX/RxSwift/blob/master/Package.swift as SPM package from Xcode, I got the question which product to add to my project.
Using the same setup as RxSwift we might be able to include the Alamofire dependency ONLY for that extension product.
To prevent errors from silently disappearing.
If no error handlers are registered, print or assert, possibly using same mechanism as #14
Current (2015-01-08) behaviour is that every callback (from map
, then
and the like) happen synchronously on the same thread as where PromiseSource.resolve
is called. Lets call this "synchronous dispatch".
Sometimes synchronous dispatch is the correct behaviour, but most of the time it leads to unexpected results.
Add support to "configure" a promise to run on a provided dispatch queue. For example:
let myQueue = dispatch_queue_create("com.example.MyQueue", nil);
getSomePromise()
.dispatchOn(myQueue)
.map { value in
// This is called on my queue
return value
}
.then { value in
// This is also called on my queue
// That means we can't do UI operations! (not UI thread)
}
The call to dispatchOn(queue: dispatch_queue_t)
returns a new Promise that is correctly configured (i.e. it doesn't mutate the existing promise). This way we can also "switch" queues:
getSomePromise()
.dispatchOn(myQueue)
.map { value in
// This is called on my queue
return value
}
.dispatchOn(dispatch_get_main_queue())
.then { value in
// This runs on main thread, so we can do UI operations
}
When nothing is configured, the main queue is chosen as a default (so users can do UI operations):
getSomePromise()
.then { value in
// Default: run on main thread
}
However, if for some reason, you want to dispatch synchronously, this can be done using dispatchSync()
:
getSomePromise()
.dispatchSync()
.then { value in
// This now runs on whatever thread `PromiseSource.resolve` was called on.
// This can be used if the caller of `resolve` expects the side effects from `then` to have happend.
}
Running a "sub computation" on a different queue is also possible, using the existing flatMap
:
getSomePromise()
.dispatchOn(myQueue)
.flatMap { value in
return Promise(value: value)
.dispatchOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0))
.map { val in
// This runs on a high priority queue
return val
}
}
.then { value in
// This is also called on my queue
// That means we can't do UI operations! (not UI thread)
}
Explain details of the different combinators, when supplied closures are run.
In particular: Explain how flatMapResult
runs on both values and errors.
Also explain when registered handlers are cleaned up.
This would be very valuable when trying to fix the error, maybe only capture/print this info in debug mode since it is a bit sensitive info.
The magic constants like __FUNCTION__
should make this possible.
Currently (2015-04-16) Promissum only uses NSError
for errors.
By changing the type Promise<T>
to Promise<Value, Error>
a custom error type can be provided.
This would allow for more type-safe, fine grained error handling.
...and related methods that can be promisified.
When using the Promissum/Alamofire
subspec you'll get the following warning:
ld: warning: linking against a dylib which is not safe for use in application extensions: /Users/[yada-yada]-iphonesimulator/Alamofire/Alamofire.framework/Alamofire
This is because Alamofire declares NOT to be safe for use in application extensions, while Promissum declares it is:
Line 20 in 9343949
We are using Promissum in a legacy project that we are trying to upgrade to Swift 3, but I found some issues when doing so.
Say that I have something like:
class Api {
// ...
fileprivate func requestJSON(_ path: Path) -> Promise<SuccessResponse<AnyObject>, ApiError> {
let url = path.url(baseUrl)
return manager
.request(url)
.responseJSONPromise()
.mapError(self.errorTransformer)
}
// MARK: Error helpers
fileprivate func errorTransformer(_ error: ErrorResponse) -> ApiError {
return .networkingError(error.result as NSError)
}
// ...
}
Where ApiError
is:
import Foundation
import Statham
public enum ApiError : Error {
case notFound
case networkingError(NSError)
case jsonDecodingError(Error)
}
extension ApiError: CustomStringConvertible {
public var description: String {
switch self {
case .notFound: return NSLocalizedString("Not Found", comment: "Api Error: resource not found")
case .networkingError(let error): return error.localizedDescription
case .jsonDecodingError(let error):
#if DEBUG
if let error = error as? JsonDecodeError {
print(error.fullDescription)
}
#endif
return NSLocalizedString("Server error", comment: "Api Error: Server error")
}
}
}
The problem comes when using .mapError()
, where the compiler complains about "Argument passed to call that takes no arguments". Digging in the github, I can see that indeed there is this following method mapError() that does not take any arguments and that is a new addition since the 0.5.0 version.
I can see however the right method here, but it seems that extension is sort of overriding the right one? Or am I missing something important here?
For consistency with other .map*
functions.
Suggested by @sdevos. See if it's possible to reduce the size of the call stack when debugging code related to promises.
It might be necessary to manually inline a lot of code, making the internals of Promissum a bit uglier. Let's investigate to see if this is worth the trouble.
This message: PromiseSource.deinit: WARNING: Unresolved PromiseSource deallocated, maybe retain this object?
appears in our test suite log often, which makes the test results difficult to read. The warning is useful in a production context, but not while testing. It would be nice to be able to disable the warning globally. Although we could disable the warning on an instance-by-instance basis, we would prefer to not need to change the construction of promises throughout the codebase.
Include at least:
map
, flatMap
, then
, and PromiseSource
)public func whenAllResult<Value, Error>(promises: [Promise<Value, Error>]) -> Promise<[Result<Value, Error>], NoError> {
let source = PromiseSource<[Result<Value, Error>], NoError>()
var results = promises.map { $0.result }
var remaining = promises.count
if remaining == 0 {
source.resolve([])
}
for (ix, promise) in promises.enumerate() {
promise
.finallyResult { result in
results[ix] = result
remaining = remaining - 1
if remaining == 0 {
source.resolve(results.map { $0! })
}
}
}
return source.promise
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.