1

I am having trouble with this code:

// var env_array = ["env1", "env2", "env3", "env4"];

Promise.all(env_array.map(function(env) {
    return device_get_env(env).catch(function(err) { return err });
})).then(function(data) {
    console.log(data);
    data.forEach(function(entry) {
        console.log(entry.data.connected);
    });
}).catch(function(data) {
    console.log(data);
});


function device_get_env(env) {
    var env = ...;
    var device_id = ...;

    return get_token_data(env, 0).then(function(data) {

        var url_base = ... ;

        return $.ajax({
            url: url_base,
            method: "GET",
            dataType: 'json',
            headers: {Authorization: data.token_type + " " + data.access_token}
        });
    });
}


function get_token_data(env, auth) {
    var client_id = env_tokens[env].client_id;
    var client_secret = env_tokens[env].client_secret;
    var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";

    return $.ajax({
        url: "https://" + env + ".xxxx.com/oauth/token",
        method: "POST",
        data: {
            "client_id": client_id,
            "client_secret": client_secret,
            "audience": audience,
            "grant_type": "client_credentials"
        },
        dataType: 'json'
    });
}

Basically I need to iterate over env_array and find device items in some of my environments.

device_get_env() returns AJAX call, which could be a success/200 or error/404.

Thus my Promises.all won't return unless all promises are resolved.

I've been digging how to overcome this.

Been trying to implement this solution: https://stackoverflow.com/a/30378082/1913289, but I'm having this error here:

TypeError: device_get_env(env).catch is not a function. (In 'device_get_env(env).catch(function(err) {return err} )', 'device_get_env(env).catch' is undefined)

Any way to solve this with my code here?

UPD: implementation suggested by @Bergi

function get_token_data(env, auth) {
    var client_id = env_tokens[env].client_id;
    var client_secret = env_tokens[env].client_secret;
    var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";

    return Promise.resolve(
        $.ajax({
            url: "https://" + env + ".xxxx.com/oauth/token",
            method: "POST",
            data: {
                "client_id": client_id,
                "client_secret": client_secret,
                "audience": audience,
                "grant_type": "client_credentials"
            },
            dataType: 'json'
        })
    )
}


function device_get_env(env) {
    var env = ...;
    var device_id = ...;

    return get_token_data(env, 0).then(function(data) {
        var url_base = ... ;

        return Promise.resolve(
            $.ajax({
                url: url_base,
                method: "GET",
                dataType: 'json',
                headers: { Authorization: data.token_type + " " + data.access_token }
            })
        )
    });
}


Promise.all(env_array.map(function(env) {
    return device_get_env(env).then(null, function(err) { return err });
})).then(function(data) {
    console.log(data);
}).catch(function(data) {
    console.log(data);
});

UPD1:

Promise.all(env_array.map(function(env) {
    return device_get_env(env).catch(function(err) {return err} );
})).then(function(data) {
    console.log(data);
}).catch(function(data) {
    console.log(data);
});

FINAL UPD: I used fetch: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Supplying_request_options

to replace my AJAX http requests.

The Promise returned from fetch() won’t reject on HTTP error status even > if the response is an HTTP 404 or 500. Instead, it will resolve normally > (with ok status set to false), and it will only reject on network failure > or if anything prevented the request from completing.

Promise.all example:

Promise.all(env_array.map(function(env) {
    return device_get_env(env);
})).then(function(data) {
    console.log(data);
}).catch(function(data) {
    console.log(data);
});

fetch example:

function get_token_data(env, auth) {
    var client_id = env_tokens[tenant].client_id;
    var client_secret = env_tokens[env].client_secret;
    var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";

    var url_base = "https://" + env + ".xxxx.com/oauth/token";
    var myInit = {
        method: 'POST',
        mode: 'cors',
        dataType: 'json',
        cache: 'default',
        data: {
            "client_id": client_id,
            "client_secret": client_secret,
            "audience": audience,
            "grant_type": "client_credentials"
        }
    };

    return fetch(url_base, myInit);
}
Community
  • 1
  • 1
sevenfourk
  • 463
  • 4
  • 12
  • At first glance is just seems that you are not returning a promise from your`get_token_data` function (which is why you are getting the error about `device_get_env` not having a `catch()` function. Can you post all the relevant code, including your `get_token_data` function? – radiovisual Mar 08 '17 at 14:23
  • radiovisual, updated code block. That is basically also an AJAX return function. – sevenfourk Mar 08 '17 at 14:29

2 Answers2

3

Your problem is that device_get_env(env) returns a jQuery promise, not an ES6 promise. And those don't have a .catch() method until v3.0

To overcome this issue, you can either

  • update jQuery,
  • dodge the jQuery promise by doing return Promise.resolve($.ajax(…)) in get_token_data, or
  • use .then(null, function(err) { return err }) instead of .catch(…)
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • will that also process resolved and rejected promises? I'm currently getting first rejected promise and could not get my promises return array to work with. – sevenfourk Mar 08 '17 at 17:08
2

Your first clue that you aren't returning a Promise somewhere in your code is in your error:

TypeError: device_get_env(env).catch is not a function.

The error is telling you that you are trying to call .catch() on an object that doesn't have a catch() method on it (even though AJAX objects are technically "thenable", earlier versions of jQuery don't have a catch() method. So one solution is to upgrade your version of jQuery1.

Your particular problem is that you are not getting the ES6 Promise you expect from the AJAX call in your get_token_data() function, instead, you are returning a jQuery Promise.

So if you wrap/cast the jQuery promise to a catch()-able ES6 Promise via Promise.resolve() in get_token_data() then you should be on the right track:

function get_token_data(env, auth) {
  var client_id = env_tokens[env].client_id;
  var client_secret = env_tokens[env].client_secret;
  var audience = auth == 1 ? "https://" + env + ".xxxx.com/api/v2/" : "yyyy.com";

  var ajaxOptions = {
    url: "https://" + env + ".xxxx.com/oauth/token",
    method: "POST",
    dataType: 'json',
    data: {
      "client_id": client_id,
      "client_secret": client_secret,
      "audience": audience,
      "grant_type": "client_credentials"
    }
  };

  // here is where the magic happens to cast your 
  // jQuery Promise into a 'catchable' Promise 
  return Promise.resolve($.ajax(ajaxOptions));
};

I can't test this code, so you might need to tweak it, but that is the basic idea.

Hope this helps!

1 Thanks to @Bergi for reminding me of this.

Community
  • 1
  • 1
radiovisual
  • 6,298
  • 1
  • 26
  • 41
  • radiovisual, yeah, you were right. I also added promise return to device_get_env() and now I get all the responses for resolved and rejected promises. – sevenfourk Mar 08 '17 at 14:57
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! jQuery ajax returns a thenable already! – Bergi Mar 08 '17 at 15:18
  • 1
    Thanks @bergi, I should have caught that. I will edit the answer to reflect that so that it does not lead someone astray in the future. :) – radiovisual Mar 08 '17 at 15:42
  • 1
    @Bergi, I have refactored to save our future generations from falling into the Promise constructor anti-pattern. And for that, my friend, you are a hero! – radiovisual Mar 08 '17 at 18:34
  • @radiovisual, thanks for wrapping the code here. But I'm getting first error and not having any promise.all array with the example code in *UPD1*. Just getting single 404 error and that's all. – sevenfourk Mar 08 '17 at 19:23
  • @sevenfourk, that is because [Promise.all](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) will stop running as soon as one of the Promises rejects. From [Mozilla](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all): *If any of the passed in promises rejects, the all Promise immediately rejects with the value of the promise that rejected, ignoring all the other promises whether or not they have resolved.* – radiovisual Mar 09 '17 at 06:48