【问题标题】:Promise.allSettled in babel ES6 implementationPromise.allSettled 在 babel ES6 实现中
【发布时间】:2015-08-14 15:47:16
【问题描述】:

我正在使用 babel 转换我的 node.js@0.10.x 代码,但我被承诺所困。

我需要 allSettled 类型的功能,例如可以在 qbluebirdangular.$q 中使用。

在 babel 的 core-js Promise 上,没有 allSettled 方法。

目前我正在使用q.allSettled 作为解决方法:

import { allSettled } from 'q';

在 babel polyfill 中有类似的东西吗?或者,哪种算法适合我尝试实现?

【问题讨论】:

    标签: javascript node.js ecmascript-6 babeljs es6-promise


    【解决方案1】:

    2019 年答案

    a proposal将此功能添加到ECMAScript标准中,已被接受!详情请查看Promise.allSettled docs

    原答案

    如果您看一下implementation of q.allSettled,您会发现它实际上很容易实现。以下是使用 ES6 Promises 实现它的方法:

    function allSettled(promises) {
        let wrappedPromises = promises.map(p => Promise.resolve(p)
            .then(
                val => ({ status: 'fulfilled', value: val }),
                err => ({ status: 'rejected', reason: err })));
        return Promise.all(wrappedPromises);
    }
    

    【讨论】:

    • Michael - 我也在寻找这样的解决方案(Promise.all 执行所有承诺,然后捕获数组中的所有错误) - 为了使用您的 allSettled 函数,将我做了一个 npm install q 然后做 const q = require('q');在我的应用程序中?
    • @RogerDodger 我编写的代码假设您正在运行支持 ES6 的运行时,因此基本上任何当前的浏览器或现代版本的 Node.js 都可以运行。或者你正在使用一个 promise polyfill。如果是这种情况,那么您不需要导入 Q 或任何其他承诺库。
    • 该死,2020 年 2 月 Edge 仍然不支持 Promise.allSettled,感谢您原始答案中的 polyfill!
    • 为什么要在Promise.resolve(p) 中封装一个promise?确定这绝对是一个承诺?
    • @OlehZiniak 是的,完全正确。如果您有一组混合的承诺和非承诺值,可能会很方便。在这种情况下,另一种选择是让 allSettled 抛出错误。
    【解决方案2】:

    2020 年答案:

    其他答案试图做的是自己实现Promise.allSettledcore-js 项目已经完成了这项工作。

    你需要做的是通过core-js为你制作babel polyfill Promise.allSettled。您配置它的方式是通过@babel/preset-env 像这样:

    presets: [
        ['@babel/preset-env', {
            useBuiltIns: 'usage',
            corejs: {version: 3, proposals: true},
        }],
    ],
    

    在您的构建工件中,这将添加对require("core-js/modules/esnext.promise.all-settled") 的调用,该调用将.allSettled 函数猴子补丁到promise API。

    【讨论】:

    • 这就是答案!
    【解决方案3】:
    const allSettled = promises =>
      Promise.all(promises.map(promise => promise
        .then(value => ({ state: 'fulfilled', value }))
        .catch(reason => ({ state: 'rejected', reason }))
      ));
    

    或者如果你坚持使用 polyfill:

    if (Promise && !Promise.allSettled) {
      Promise.allSettled = function (promises) {
        return Promise.all(promises.map(function (promise) {
          return promise.then(function (value) {
            return { state: 'fulfilled', value: value };
          }).catch(function (reason) {
            return { state: 'rejected', reason: reason };
          });
        }));
      };
    }
    

    取自here

    【讨论】:

    • 这很好(我正在使用它)但是这个 polyfill 中有一个错误。该属性返回should be status and not state。我在这里发出了一个拉取请求,其中包含仍然向后兼容的 polyfill 的修改版本(但有一个理想的版本,它也缺少 PR 中的 state 字段):github.com/robhicks/es2015-Promise.allSettled/pull/1
    • @chunk_split 谢谢,我将core.js 升级到了v3,所以现在我有了原生allSettled,不再需要polyfill。
    【解决方案4】:

    或者,我可以尝试实施哪种算法?

    1. 使用executor function 创建一个新的承诺
    2. 在执行器范围内使用计数器/结果数组
    3. 向每个父承诺注册一个 then() 回调,将结果保存在数组中
    4. 当计数器指示所有父级承诺都已完成时,从第 1 步解决/拒绝承诺

    【讨论】:

    • 很好,不过现在我会坚持使用 Q(可能也是这样做的)。
    • 关于你的第 4 步,我怎么知道拒绝新的承诺?
    • @torazaburo 你是对的,你永远不会拒绝,这就是 allSettled 的意义 :) 也许有些超时,但这是一个不同的用例,并不能反映 q.allSettled 的作用。
    【解决方案5】:

    这是我对类似事情的尝试,我有 Newsletter 服务,在我的情况下,我希望我的 allSettled 承诺通过所有结果(拒绝和解决)的数组来解决,按顺序,一次email_promises 已解决(所有电子邮件都已发送):

    Newsletter.prototype.allSettled = function(email_promises) {
        var allSettledPromise = new Promise(function(resolve, reject) {
            // Keep Count
            var counter = email_promises.length;
    
            // Keep Individual Results in Order
            var settlements = [];
            settlements[counter - 1] = undefined;
    
            function checkResolve() {
                counter--;
                if (counter == 0) {
                    resolve(settlements);
                }
            }
    
            function recordResolution(index, data) {
                settlements[index] = {
                    success: true,
                    data: data
                };
                checkResolve();
            }
    
            function recordRejection(index, error) {
                settlements[index] = {
                    success: false,
                    error: error
                };
                checkResolve();
            }
    
            // Attach to all promises in array
            email_promises.forEach(function(email_promise, index) {
                email_promise.then(recordResolution.bind(null, index))
                    .catch(recordRejection.bind(null, index));
            });
        });
        return allSettledPromise;
    }
    

    【讨论】:

      【解决方案6】:

      以下是相同功能的另一个版本:spex.batch

      source code 太多了,无法在此处重新发布,所以这里只是一个来自batch processing 的示例,说明如何使用它:

      var spex = require('spex')(Promise);
      
      // function that returns a promise;
      function getWord() {
          return Promise.resolve("World");
      }
      
      // function that returns a value;
      function getExcl() {
          return '!';
      }
      
      // function that returns another function;
      function nested() {
          return getExcl;
      }
      
      var values = [
          123,
          "Hello",
          getWord,
          Promise.resolve(nested)
      ];
      
      spex.batch(values)
          .then(function (data) {
              console.log("DATA:", data);
          }, function (reason) {
              console.log("REASON:", reason);
          });
      

      这个输出:

      DATA: [ 123, 'Hello', 'World', '!' ]
      

      现在让我们通过将getWord 更改为以下内容来使其失败:

      function getWord() {
          return Promise.reject("World");
      }
      

      现在的输出是:

      REASON: [ { success: true, result: 123 },
        { success: true, result: 'Hello' },
        { success: false, result: 'World' },
        { success: true, result: '!' } ]
      

      即整个数组已解决,报告索引绑定结果。

      如果我们不报告全部原因,而是致电getErrors()

      console.log("REASON:", reason.getErrors());
      

      那么输出将是:

      REASON: [ 'World' ]
      

      这只是为了简化对已发生错误列表的快速访问。

      【讨论】:

        【解决方案7】:

        我的实现将在下面

        Promise.prototype.myAllSettled = function (arr = []) {
          return new Promise(function processIterable(resolve, reject) {
            let result = [];
            arr.forEach((item) => {
              item
                .then((value) => {
                  result.push({ status: "fulfilled", value: value });
                  if (arr.length === result.length) resolve(result);
                })
                .catch((err) => {
                  result.push({ status: "rejected", reason: `${err}` });
                  if (arr.length === result.length) resolve(result);
                });
            });
          });
        };
        

        【讨论】:

        • 嗨 Vikas,这是一个有点老的问题。现在你甚至不需要一个库,所有主流浏览器都支持它:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
        • 但不是电子版
        • 这有一个问题,它根据首先完成的承诺重新排序承诺,这不是本机函数的行为方式
        猜你喜欢
        • 2018-06-29
        • 2015-11-28
        • 2016-03-07
        • 1970-01-01
        • 2020-08-25
        • 1970-01-01
        • 2016-05-26
        • 2016-06-05
        • 2016-12-19
        相关资源
        最近更新 更多