1

The question is hard to describe so let me post an example. The context is AngularJS $q, but any solutions based on ES6 Promise are also welcome.

Suppose we want to cancel an $http request. In AngularJS this is implemented by providing a promise A to $http.get() method. Resolve promise A will result in cancelling the request. So the code will look like this:

function httpCall() {
    const promiseA = $q.defer();
    const promiseB = $http.get(url, { timeout: promiseA.promise });
    return promiseB;
}

In order to return promiseA so that the caller of httpCall can cancel the request, the only way I found is to assign it to promiseB, like this:

function httpCall() {
    const promiseA = $q.defer();
    let promiseB = $http.get(url, { timeout: promiseA });
    promiseB.canceller = promiseA;
    return promiseB;
}

The problem is, promiseB may be passed through a long promise chain (such as action dispatch, mapping, other post-processes), for example:

function doSomeAction() {
    return httpCall()
        .then((response) => {
            processResponse(response);
            return response;
        });
}

Obviously, the return value of doSomeAction is NOT the same promise as the origin promise promiseB.

Thus if I have a function who calls doSomeAction(), this function will not be able to retrieve promiseB (it can only get the return promise of doSomeAction()), thus not able to cancel the request.

Someone may say we can pass the data (promiseA) through the chain. However I consider the existance of promiseA should be transparent to the chain itself, i.e. the functions in the chain should never know the existance of promiseA. Thus I cannot depend on the chain to pass data for me.

Any suggestions are welcome.

1
  • 1
    The timeout property of the $http config should be a promise. The variable promiseA is not a promise object; it is a deferred. A promise object is $q.deferred().promise. Commented May 2, 2018 at 19:33

1 Answer 1

3

Not knowing about promiseA makes total sense as things are happening on the different levels of abstraction. doSomeAction abstracts out http call and providing any API which cancels underlying http call breaks an abstraction and reveal doSomeAction's implementation details. Instead I would think about making doSomeAction cancellable i.e. providing a cancel method on an object it returns. This cancel method can cancel underlying http request (or several of them if there are several) or skip cancelling if no http request was made and data was taken from local storage or do whatever makes sense for doSomeAction, not just particular http request which is just part of a bigger picture on this layer.

Sign up to request clarification or add additional context in comments.

1 Comment

That totally makes sense when considering the chain may be complicated (e.g. $q.all()). While I'm still trying to find out how to make it generic, I'll take this as the last solution. Thank you @nutic

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.