0

I am using angularJS and trying to creating amazon VPC through aws.sdk.js

Now when I create VPC I have to done various configuration and call below methods in chain

  1. create vpc () => vpc_
  2. create internet gateway () => igw_
  3. attach internet gateway (vpc_, igw_) =>
  4. create subnet (vpc_) => subnet_
  5. create route table (vpc_) => rtb_
  6. associate route table (rtb_, subnet_) =>
  7. create security group (vpc_) => sg_
  8. add inbound rule (sg_) =>

  9. additionally create tags for each generated resources .

as you can see some function parameter depends on previous function and so on. I am using $q services on each AWS method but this is became so large callback hell.

I also capture notifications of each function in back so my input function became like this

functionOne().then(function(res1) {
   console.info(res1);
   functionTwo(res1.id).then(res2) {
      console.info(res2);
       ......... series of functions within function....
        ----------------
        ----------------
        .then(res7) { functionEight(res7.id) {
        }, function (err) {}, function (notify) {})
        .finally(function(){ console.log('Finally done everything'); });
       ---------------------
       ---------------------
   }, function (err2) {
  console.error(err2);
  }, function (notify2) {
   console.log(nofify2);
  });
}, function (err1) {
  console.error(err1);
}, function (notify1) {
   console.log(nofify1);
});

my function signature is as below

 functionX: function() {
                    var d = $q.defer();
                    var params = {
                        //...
                    };
                    ec2.anyMethodName(params, function(err, data) {
                        if (err) {
                            d.reject(err);
                        } else {
                            d.notify('Notifications methods');
                            d.resolve(data);
                        }
                    });
                    return d.promise;
                }

You can see the chaining of methods in my controller here

So my question is

  • Does Amazon native thenable promise property can be used in angular? if yes then how do we implement notifications?

  • Is there any sorter way to do that? like error will be captures in last? using $.all() but do not not how?

  • If i use .finally() on functionOne than it throw error in console .finally is not a function

Cœur
  • 37,241
  • 25
  • 195
  • 267
xkeshav
  • 53,360
  • 44
  • 177
  • 245

2 Answers2

1

Why don't you

functionOne()
    .catch(errorHandlerOne)
    .then(functionTwo) // calls functionTwo(responseOne)
    .catch(errorHandlerTwo)
    .then(functionThree) // calls functionThree(responseTwo)
    .catch(errorHandlerThree)
    .finally(finalHandler)

Edit: If you want to access the result of functionOne in functionThree, you can do the following:

functionTwo(resultOne) {
    var promise = d.promise;
    promise.then(function(resultTwo) {
       return [resultOne, resultTwo];
    });
  return d.promise
}

Refer to this answer for more details.

You don't need to necessarily have to write anonymous callbacks as promise resolve and reject handlers

Your callbacks will be called with the resolved/rejected promise from the previous function automatically.

If you want your entire chain to fail if any one promise fails, you call always use .all(), so that's something you should pay attention to if you want to use .all().

Community
  • 1
  • 1
nikjohn
  • 20,026
  • 14
  • 50
  • 86
  • and where do i get the notifications sent via `d.notify()` written inside each function? – xkeshav Oct 21 '16 at 10:13
  • see in my question? in method 3 I need parameter as the result of method 1 and method 2. as method 2 does not require any parameter . – xkeshav Oct 21 '16 at 10:15
  • Does 1.all also called in series or parallel? can you please highlight how to use `$.all` – xkeshav Oct 21 '16 at 10:19
  • When you chain promises in the fashion that I've mentioned, the result of previous promises will be passed as the argument to your current promise. `functionThree` will _by default_ be passed an argument which is the resolved result of `functionTwo` when it is called in this fashion. If `functionTwo` doesn't require an argument, then you don't need to implement it at all. Just ignore the param. – nikjohn Oct 21 '16 at 10:19
  • but `functionThree` requires params from the resolve of `functionOne` and `functionTwo` – xkeshav Oct 21 '16 at 10:20
  • When you pass in a group of arrays to `.all()`, it doesn't know anything about what order the functions need to be executed. So it happens in **parallel**. Promises are _triggered_ serially, but the results don't come in any particular order – nikjohn Oct 21 '16 at 10:22
  • So `.all` is probably not the solution for you – nikjohn Oct 21 '16 at 10:23
  • If you need to access the result of `functionOne` in `functionThree`, I guess you can assign the result of `functionOne` to a variable and access in `functionThree` but that seems to be a hack. I guess you could try adding a custom field to your promise when calling it i.e. in `functionTwo`, before `return promiseThree`, just add a `promiseThree.tempData = functionOneResponse` and try and access this object in `functionThree`. I'm not sure about this though. Never tried it. I'll look around for an answer for that question. – nikjohn Oct 21 '16 at 10:31
  • Thanks @nikjohn ; i will surely look into the way you suggest. let me try – xkeshav Oct 21 '16 at 10:32
  • @pro.mean I've made an edit to the answer with your requirement – nikjohn Oct 21 '16 at 11:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126322/discussion-between-pro-mean-and-nikjohn). – xkeshav Oct 21 '16 at 11:07
1

To chain promises, return values to the success handlers; throw values to the rejection handlers:

functionOne().then(function onSuccess(res1) {
    console.info(res1);
    //return promise to chain
    return functionTwo(res1.id);
}).catch(function onReject(error1) {
    console.log(error1)
    //throw to chain rejection
    throw error1;
}).then(function onSuccess2(res2) {
    console.info(res2);
    //return promise to chain
    return functionThree(res2.id);
}).then(function onSuccess3(res3) {
    console.info(res3);
    //return promise to chain
    return functionFour(res3.id);
}).then(function onSuccess4(res4) {
    console.info(res4);
    //return to chain
    return res4.id;
});

It is very important to throw to rejection handlers. If a rejection handler simply does a console log and omits the throw statement, the function returns undefined and the rejection gets converted. The next success handled will be invoked with undefined as its argument.

From the Docs:

Chaining promises

Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.

It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs

--AngularJS $q Service API Reference -- Chaining Promises

For more information, see SO: Angular execution order with $q -- Chaining Promises


Promises from an external source such as the AWS API can be converted to a $q service with $q.when.

var angularPromise = $q.when(AWS.request.promise());

From the DOCS:

when

Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.

--AngularJS $q Service API Reference -- $q.when

Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • why you have used .catch only once? what if there is any error in `functionSix()` – xkeshav Oct 21 '16 at 11:05
  • Put a `.catch` at the end of the chain and it will catch all of the previous errors. There is no need to include a rejection handler for every item in the chain. When a promise is rejected, all the subsequent success handlers are skipped. BUT if you have a rejection handler in the middle of the chain, it is very important to include a `throw` statement. – georgeawg Oct 21 '16 at 11:23