PromiseKit, how to await a finalized promise? - swift

coming from the JS world I'm having a bit of problem wrapping my head around promise kit flavor of promises, I need a bit of help with the following.
Assume I have a function that returns a promise, say an api call, on some super class I await for that promise, then do some other action (potentially another network call), on that parent call I also have a catch block in order to set some error flags for example, so in the end I have something close to this:
func apiCall() -> Promise<Void> {
return Promise { seal in
// some network code at some point:
seal.fulfill(())
}
}
// in another class/object
func doApiCall() -> ? { // catch forces to return PMKFinalizer
return apiCall()
.done {
// do something funky here
}
.catch {
print("Could not do first request"
}
}
now I'm trying to write some unit tests for this functionality, so the response is mocked and I know it will not fail, I just need to await so I can verify the internal state of my class:
// on my test file
doApiCall().done {
// test my code, but I get an error because I cannot pipe a promise that already has a `.catch`
}
How would one go about solving this problem? I could use finally to chain the PMKFinalizer but that feels wrong
Another tangential question would be, is it possible to re catch the error on a higher level, let's say a UI component so it can hold some temporary error state? as far as I see I did not see a way to achieve this.
Many thanks 🙏

Related

Refactor catch statements

We have many try-catch blocks in our code handling the exceptions of the api calls. Since most of the catch blocks are identical we want to refactor them and only them (because the try blocks should stay in the place they are). How is this possible in Flutter?
Example code:
try {
_userData = apiService.call("user_data");
} on ResourceNotFoundException {
handleResourceNotFoundException();
} on NetworkException {
handleNetworkException();
}
The best solution I found is using a general catch, fit everything into a handleException function, and in there rethrow the exception again.
try {
_userData = apiService.call("user_data");
} on catch (e) {
handleException(e);
}
void handleException(e) {
try {
throw e;
} on ResourceNotFoundException {
handleResourceNotFoundException();
} on NetworkException {
handleNetworkException();
}
}
This way it is possible to reuse the exception handling logic and also extend it.
You should add some abstraction to your API call, meaning you should add a function that takes in the API call you are trying to call as a parameter and surround it with a try-catch block and handle all your exceptions there.
This way you have separated your API calls logic from handling exceptions.

How to do a `getOrElseComplete` on `Promise`?

Does it make sense to have an operation like getOrElseComplete that tries to complete a Promise with a value but, if the Promise is already completed, returns the existing completed value instead. Here's a sample implementation:
implicit class PromiseOps[T](promise: Promise[T]) {
def getOrElseComplete(value: Try[T]): Try[T] = {
val didComplete = promise.tryComplete(value)
if (didComplete) {
value
} else {
// The `tryComplete` returned `false`, which means the promise
// was already completed, so the following is safe (?)
promise.future.value.get
}
}
}
Is this safe to do? If not, why not? If so, is there a way to do this directly (eg. without relying on questionable things like _.value.get) or why isn't there such a way in the standard library?
From your comments it seems to me that this is a valid solution for your problem but I also feel that a method like this doesn't belong in Promise API because Promise is supposed to be only a settable "backend" of its Future.
I'd prefer to have a completely separate function which could look like this:
def getOrComplete[T](promise: Promise[T], value: Try[T]): Try[T] =
promise.future.value.getOrElse {
if (promise.tryComplete(value)) value
else getOrComplete(promise, value)
}
The recursive call may seem weird - it serves two purposes:
it protects against a race condition where some other thread completes the future just before we call tryComplete
it avoids usage of .value.get on the Future
You might also want to pass value as a by-name parameter to avoid evaluating it when the Promise is already completed.
This operation does what it promises. It may make more sense to take value by name, and don't try to complete if already completed, maybe something like
def getOrElseComplete(value: => Try[T]): Try[T] = {
if (!promise.completed) {
promise.tryComplete(value)
}
promise.future.value.get
}
It's kinda dodgy though. Sharing a promise and having multiple places where it might be completed sounds like a difficult to maintain design, and one has to ask what's happening with the other path that might still complete the Promise? Shouldn't something be cancelled there?

How can I chain promises in Swift inside a loop?

I'm building a Swift-based iOS application that uses PromiseKit to handle promises (although I'm open to switching promise library if it makes my problem easier to solve). There's a section of code designed to handle questions about overwriting files.
I have code that looks approximately like this:
let fileList = [list, of, files, could, be, any, length, ...]
for file in fileList {
if(fileAlreadyExists) {
let overwrite = Promise<Bool> { fulfill, reject in
let alert = UIAlertController(message: "Overwrite the file?")
alert.addAction(UIAlertAction(title: "Yes", handler: { action in
fulfill(true)
}
alert.addAction(UIAlertAction(title: "No", handler: { action in
fulfill(false)
}
} else {
fulfill(true)
}
}
overwrite.then { result -> Promise<Void> in
Promise<Void> { fulfill, reject in
if(result) {
// Overwrite the file
} else {
// Don't overwrite the file
}
}
}
However, this doesn't have the desired effect; the for loop "completes" as quickly as it takes to iterate over the list, which means that UIAlertController gets confused as it tries to overlay one question on another. What I want is for the promises to chain, so that only once the user has selected "Yes" or "No" (and the subsequent "overwrite" or "don't overwrite" code has executed) does the next iteration of the for loop happen. Essentially, I want the whole sequence to be sequential.
How can I chain these promises, considering the array is of indeterminate length? I feel as if I'm missing something obvious.
Edit: one of the answers below suggests recursion. That sounds reasonable, although I'm not sure about the implications for Swift's stack (this is inside an iOS app) if the list grows long. Ideal would be if there was a construct to do this more naturally by chaining onto the promise.
One approach: create a function that takes a list of the objects remaining. Use that as the callback in the then. In pseudocode:
function promptOverwrite(objects) {
if (objects is empty)
return
let overwrite = [...] // same as your code
overwrite.then {
do positive or negative action
// Recur on the rest of the objects
promptOverwrite(objects[1:])
}
}
Now, we might also be interested in doing this without recursion, just to avoid blowing the call stack if we have tens of thousands of promises. (Suppose that the promises don't require user interaction, and that they all resolve on the order of a few milliseconds, so that the scenario is realistic).
Note first that the callback—in the then—happens in the context of a closure, so it can't interact with any of the outer control flow, as expected. If we don't want to use recursion, we'll likely have to take advantage of some other native features.
The reason you're using promises in the first place, presumably, is that you (wisely) don't want to block the main thread. Consider, then, spinning off a second thread whose sole purpose is to orchestrate these promises. If your library allows to explicitly wait for a promise, just do something like
function promptOverwrite(objects) {
spawn an NSThread with target _promptOverwriteInternal(objects)
}
function _promptOverwriteInternal(objects) {
for obj in objects {
let overwrite = [...] // same as your code
overwrite.then(...) // same as your code
overwrite.awaitCompletion()
}
}
If your promises library doesn't let you do this, you could work around it by using a lock:
function _promptOverwriteInternal(objects) {
semaphore = createSemaphore(0)
for obj in objects {
let overwrite = [...] // same as your code
overwrite.then(...) // same as your code
overwrite.always {
semaphore.release(1)
}
semaphore.acquire(1) // wait for completion
}
}

How to get a CycAccess object?

There's a lisp function in ResearchCyc called random-assertion. I want to call that from some Java code. I'm using the Cyc Core API Suite v1.0.0-rc5 (from http://dev.cyc.com), but I don't see any way to call underlying Lisp code.
In the old OpenCyc API there was an object called CycAccess that you could use for this, but I can't figure out how to get one. If I could find it, I'd call this
access.converseObject("(random-assertion)");
At least in ResearchCyc, this would retrieve a pseudo-random assertion from the Cyc knowledge base. Not sure if it would work in OpenCyc, but it might also work there.
Can someone explain how to call lisp-code like this through Cyc's java API?
(Disclaimer: I am one of the developers of the Cyc APIs...)
The reference implementation of the Core API Suite is the Core Client, which is based on the Base Client... which, in turn, is derived from the old OpenCyc API. So, it's absolutely possible to call arbitrary lisp (SubL) code on ResearchCyc, in several different ways...
First of all, there already is a method which wraps random-assertion:
try {
CycAccess access = CycAccessManager.getCurrentAccess();
CycAssertion cycAssertion = access.getLookupTool().getRandomAssertion();
} catch (SessionException ex) {
// Do something with the exception...
} catch (CycConnectionException connEx) {
// Do something else...
}
Speaking to the general case, though, you'll find that the syntax is fairly similar to the OpenCyc API:
try {
CycAccess access = CycAccessManager.getCurrentAccess();
Object cycAssertion = access.converse().converseObject("(random-assertion)");
} catch (SessionException ex) {
// Do something with the exception...
} catch (CycConnectionException connEx) {
// Do something else...
}
Or, if it's safe to assume that the result will be a CycObject:
...
CycAccess access = CycAccessManager.getCurrentAccess();
CycObject cycAssertion = access.converse().converseCycObject("(random-assertion)");
...
However, the Base Client adds a new way of encapsulating SubL functions, via the com.cyc.baseclient.subl.SublFunction interface. The SublFunction interface itself is pretty minimal, but there are a number of classes under com.cyc.baseclient.subl.subtypes which provide implementations for you to extend. For example, if you're calling a no-arg function and expect back a CycObject, you could extend SublCycObjectNoArgFunction like so:
public static final SublCycObjectNoArgFunction RANDOM_ASSERTION_FUNCTION =
new SublCycObjectNoArgFunction("random-assertion");
...
try {
CycAccess access = CycAccessManager.getCurrentAccess();
CycObject cycAssertion = RANDOM_ASSERTION_FUNCTION.eval(access);
} catch (SessionException ex) {
// Do something with the exception...
} catch (CycConnectionException connEx) {
// Do something else...
}
(For other examples of this, see com.cyc.baseclient.subl.functions.* in the Base Client.)
This approach makes it fairly simple to define SubL functions as static fields without writing (or re-writing) much code. We expect the Core Client to gradually migrate towards this approach.
Lastly, you can use the implementation classes in the KB Client to convert your results to KBObjects. For example:
try {
CycAccess access = CycAccessManager.getCurrentAccess();
CycObject cycAssertion = RANDOM_ASSERTION_FUNCTION.eval(access);
// To convert to a com.cyc.kb.Assertion:
Assertion assertion = AssertionImpl.get(cycAssertion);
// Or, to convert to a more general KBObject:
KbObject kbObj = KbObjectImpl.get(cycAssertion);
} catch (SessionException ex) {
// Do something with the exception...
} catch (CycConnectionException connEx) {
// Do something else...
} catch (KbTypeException ex) {
// Potentially thrown by AssertionImpl#get() & KbObjectImpl#get()
} catch (CreateException ex) {
// Also potentially thrown by AssertionImpl#get() & KbObjectImpl#get()
}

Good syntax for anonymous function that is called conditionally

I am probably looking for something that is impossible, but anyway let's give it a try. Please consider the following pseudo code that is performing some conditional, remote operation, that executes callback upon completion. But the code in the callback needs to be executed even if remote operation was not neccessary:
if (needsToSave)
{
performRemoteOperation(operationParameters, function() {
doSomeCleanup();
doSomeMoreCleanup();
setSomeStatus();
});
}
else
{
doSomeCleanup();
doSomeMoreCleanup();
setSomeStatus();
}
I find this code particularly ugly and unmanageable. It is easy to omit a change done to callback block in relevant unconditional block. There is an obvious solution of wrapping code in some named function, but it isn't anonymous inline code anymore then. :-)
The best I can think of is to wrap whole code in some conditional caller:
function conditionalCall(condition, action, callback)
{
if (condition)
action(callback)
else
callback()
}
Then my code would fold to:
conditionalCall(needsToSave,
function(_callback) {
performRemoteOperation(operationParameters, _callback)
},
function()
{
doSomeCleanup();
doSomeMoreCleanup();
setSomeStatus();
}
);
...but I am not absolutely sure, whether this is more readable and manageable. Especially when lots of local/remote/callback parameters/closure variables get involved or one needs to "embed" one remote call within another call's callback. I hope there is some better syntax that could be used in such a scenario.
In can be simpified:
var finallyFunction = function {
doSomeCleanup();
doSomeMoreCleanup();
setSomeStatus();
}
if (needsToSave)
performRemoteOperation(operationParameters, finallyFunction);
else
finallyFunction();
This isn't really a closure problem. Assuming "remote operation" to mean "asynchronous operation", then it's to do with handling asynchronous responses.
For sure, anonymous function(s) can be (and typically will be) employed in this kind of situation, but remember that "anonymous function" is not a synonym for "closure". Forgegt (almost) everything you learned in PHP, which is not a great learning ground for lexical closures.
If my assumption is correct, and we are indeed talking about asynchronicity, then jQuery's Deferreds/promises make for a rather neat solution.
// First make sure performRemoteOperation() returns a promise,
function performRemoteOperation(operationParameters) {
...
return promise;//for example a jqXHR object, as generated by $.ajax() and its shorthand methods.
}
function myFunction(needsToSave) {
var p = needsToSave ? performRemoteOperation(operationParameters) : $.Deferred.resolve().promise();
//At this point `p` is either an unresolved promise returned by performRemoteOperation(), or a resolved promise generated in the line above.
p.done(function() {
//This function fires when the remote operation successfully completes, or immediately if `needsToSave` was false.
doSomeCleanup();
doSomeMoreCleanup();
setSomeStatus();
});
return p;//A promise is returned for good measure, in case further chaining is necessary where myFunction() is called.
}
//Sample calls
myFunction(false);
myFunction(true).then(function() {
alert("successfully saved");
}, function() {
alert("error: something went wrong");
});
Of course, you could refactor the code into a single function if you wished but it's arguably easier to understand as two functions, as indicated in the question.