【问题标题】:Promise.all(): Return a result after all Promises are resolved and/or rejectedPromise.all():在所有 Promise 都解决和/或拒绝后返回一个结果
【发布时间】:2017-07-29 03:38:38
【问题描述】:

这段代码有问题:

// 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'
    });
}

基本上我需要遍历env_array 并在我的一些环境中查找设备项。

device_get_env() 返回 AJAX 调用,可能是成功/200 或错误/404。

因此,除非所有承诺都得到解决,否则我的 Promises.all 不会返回。

我一直在研究如何克服这个问题。

一直在尝试实施此解决方案:https://stackoverflow.com/a/30378082/1913289,但我在这里遇到此错误:

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)

有什么办法可以用我的代码解决这个问题吗?

UPD:@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);
});

最终更新: 我用 fetch:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Supplying_request_options

替换我的 AJAX http 请求。

即使响应是 HTTP 404 或 500,从 fetch() 返回的 Promise 也不会拒绝 HTTP 错误状态。相反,它将正常解析 >(将 ok 状态设置为 false),它只会拒绝网络故障 > 或任何阻止请求完成的情况。

Promise.all 示例:

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

获取示例:

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);
}

【问题讨论】:

  • 乍一看,您似乎没有从您的get_token_data 函数返回一个承诺(这就是为什么您收到关于device_get_env 没有catch() 函数的错误。你能发布所有相关代码,包括您的get_token_data 函数?
  • radiovisual,更新的代码块。那基本上也是一个AJAX返回函数。

标签: javascript promise


【解决方案1】:

您的问题是 device_get_env(env) 返回一个 jQuery 承诺,而不是 ES6 承诺。还有那些don't have a .catch() method until v3.0

要解决这个问题,您可以

  • 更新 jQuery,
  • dodge the jQuery promise 通过在 get_token_data 中执行 return Promise.resolve($.ajax(…)),或
  • 使用.then(null, function(err) { return err }) 而不是.catch(…)

【讨论】:

  • 这是否也会处理已解决和已拒绝的承诺?我目前第一个被拒绝的承诺,无法让我的承诺返回数组使用。
【解决方案2】:

您没有在代码中某处返回 Promise 的第一个线索是在您的错误中:

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

错误是告诉你你试图在一个没有 catch() 方法的对象上调用 .catch()(即使 AJAX 对象在技术上是“可用的”,早期版本的 jQuery 不有一个catch() 方法。所以一个解决方案是升级你的jQuery版本1

您的特殊问题是您没有从 get_token_data() 函数中的 AJAX 调用中获得您期望的 ES6 Promise,而是返回一个 jQuery Promise。

因此,如果您通过 get_token_data() 中的 Promise.resolve() 将 jQuery 承诺包装/转换为 catch()-able ES6 Promise,那么您应该走在正确的轨道上:

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));
};

我无法测试此代码,因此您可能需要对其进行调整,但这是基本思想。

希望这会有所帮助!

1感谢@Bergi reminding me of this

【讨论】:

  • radiovisual,是的,你是对的。我还向 device_get_env() 添加了 promise return,现在我得到了所有已解决和拒绝的 promise 的响应。
  • 避免使用Promise constructor antipattern! jQuery ajax 已经返回了一个 thenable!
  • 谢谢@bergi,我应该明白这一点。我将编辑答案以反映这一点,以免将来误入歧途。 :)
  • @Bergi,我已经进行了重构,以防止我们的后代陷入 Promise 构造函数反模式。为此,我的朋友,你是英雄!
  • @radiovisual,感谢您在此处包装代码。但是我遇到了第一个错误,并且没有任何带有 UPD1 中示例代码的 promise.all 数组。只是得到一个 404 错误,仅此而已。
猜你喜欢
  • 2021-08-13
  • 2017-03-14
  • 2017-03-02
  • 2016-10-03
  • 1970-01-01
  • 2020-08-06
  • 2023-03-17
  • 1970-01-01
  • 2019-05-02
相关资源
最近更新 更多