I've been converting a project from RxSwift to PromiseKit, and it's going really well. I know these aren't regarded as competitive substitutes, but it has been a bit surprising to see how much cleaner some of the code gets.
The one thing that I'm feeling some regret about letting go of is RxSwift's Scheduler abstraction. Not because it's the world's most elegant API (though it's fine), but because it introduces a layer of abstraction between the Foundation-level facilities and the kit-level facilities. This separation is important because you can't actually subclass DispatchQueue: it's a C language system that's intimately intertwined with the compiler and runtime. If you want different dispatch behavior in PromiseKit, you're pretty much stuck, as far as I can tell. (Good thing it's easy to intermix manual GCD blocks with PromiseKit-managed blocks!)
I'll cite some specific use cases below, but just to get to the point: it would be nice if PromiseKit's on arguments accepted something like a DispatchProtocol object instead of an object-type-specific DispatchQueue. It's fine if the protocol mirrors the existing DispatchQueue API - I just want to be able to roll my own.
It looks like it would be a pretty straightforward change. I'd send it in as PR, except that I can imagine some strong potential objections to the basic idea. So I thought it would be better to get a temperature reading first. The main drawback is that DispatchProtocol becomes visible in the public API. I imagine most people's initial reaction would be something like "WTF is a DispatchProtocol?" Calling it something like DispatchQueueProtocol would perhaps clarify, but that's an awfully long name. Something like Scheduler is clear and succinct, but it obscures the fact that these objects really are just DispatchQueues in 99% of cases.
I have three use cases in the current project. One is a concurrency-limited queue. You can submit as many blocks as you like, but only N will be allowed to execute concurrently. The rest have to wait until a slot opens up. This is great for cases where you need to, e.g., read 100 XML feeds, but you know that URLSession will start acting up if you just fire them all off at once. At the same time, it's perfectly reasonable to queue up 100 blocks in a DispatchQueue, so you can get away with no queueing code at all outside of a generic concurrency-limited queue implementation.
The second case is rate-limited scheduling, where one of the external APIs limits the number of network requests that can be made in a given period of time. If you go over the limit, you get a significant punishment such as a 5-minute timeout, so it's worthwhile to obey the limit stringently. It's really easy to implement this in terms of DispatchQueues, but again, you need some way to interpose your own layer.
The last case is plain old Core Data. If I understand the architecture correctly, perform and performAndWait are just thin wrappers around dispatch queues. Nevertheless, they are the designated interface and you are supposed to use them instead of trying to get your hands on the underlying DispatchQueue. It would be really nice to be able to write, e.g., done(on: myCoreDataMOC) {...} and just have it use perform* behind the scenes.
Thoughts?
I've been converting a project from RxSwift to PromiseKit, and it's going really well. I know these aren't regarded as competitive substitutes, but it has been a bit surprising to see how much cleaner some of the code gets.
The one thing that I'm feeling some regret about letting go of is RxSwift's
Schedulerabstraction. Not because it's the world's most elegant API (though it's fine), but because it introduces a layer of abstraction between the Foundation-level facilities and the kit-level facilities. This separation is important because you can't actually subclassDispatchQueue: it's a C language system that's intimately intertwined with the compiler and runtime. If you want different dispatch behavior in PromiseKit, you're pretty much stuck, as far as I can tell. (Good thing it's easy to intermix manual GCD blocks with PromiseKit-managed blocks!)I'll cite some specific use cases below, but just to get to the point: it would be nice if PromiseKit's
onarguments accepted something like aDispatchProtocolobject instead of an object-type-specificDispatchQueue. It's fine if the protocol mirrors the existingDispatchQueueAPI - I just want to be able to roll my own.It looks like it would be a pretty straightforward change. I'd send it in as PR, except that I can imagine some strong potential objections to the basic idea. So I thought it would be better to get a temperature reading first. The main drawback is that
DispatchProtocolbecomes visible in the public API. I imagine most people's initial reaction would be something like "WTF is aDispatchProtocol?" Calling it something likeDispatchQueueProtocolwould perhaps clarify, but that's an awfully long name. Something likeScheduleris clear and succinct, but it obscures the fact that these objects really are justDispatchQueues in 99% of cases.I have three use cases in the current project. One is a concurrency-limited queue. You can submit as many blocks as you like, but only N will be allowed to execute concurrently. The rest have to wait until a slot opens up. This is great for cases where you need to, e.g., read 100 XML feeds, but you know that
URLSessionwill start acting up if you just fire them all off at once. At the same time, it's perfectly reasonable to queue up 100 blocks in aDispatchQueue, so you can get away with no queueing code at all outside of a generic concurrency-limited queue implementation.The second case is rate-limited scheduling, where one of the external APIs limits the number of network requests that can be made in a given period of time. If you go over the limit, you get a significant punishment such as a 5-minute timeout, so it's worthwhile to obey the limit stringently. It's really easy to implement this in terms of
DispatchQueues, but again, you need some way to interpose your own layer.The last case is plain old Core Data. If I understand the architecture correctly,
performandperformAndWaitare just thin wrappers around dispatch queues. Nevertheless, they are the designated interface and you are supposed to use them instead of trying to get your hands on the underlyingDispatchQueue. It would be really nice to be able to write, e.g.,done(on: myCoreDataMOC) {...}and just have it useperform*behind the scenes.Thoughts?