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); } 
2
  • At first glance is just seems that you are not returning a promise from yourget_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? Commented Mar 8, 2017 at 14:23
  • radiovisual, updated code block. That is basically also an AJAX return function. Commented Mar 8, 2017 at 14:29

2 Answers 2

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(…)
Sign up to request clarification or add additional context in comments.

1 Comment

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.
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.

6 Comments

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.
Avoid the Promise constructor antipattern! jQuery ajax returns a thenable already!
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. :)
@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, 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.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.