【问题标题】:Simplifying a Promise Chain简化承诺链
【发布时间】:2016-10-19 08:04:36
【问题描述】:

我试图通过让函数处理一组函数来尽可能简化 Promise 链。它们需要按顺序排列,但我为它们提供正确语法的方法不起作用,我确信有更好的方法来做到这一点。链中的每个函数都调用数据库,需要在返回时触发下一个函数。

// Run each in order.
var Chain = function(chain, cb){
    chain.forEach(function(i){  i = function(r){return new Promise(i)};   });
    chain.reduce(function(cur, next) { {cur.then(next)} }, Promise.resolve()).then(function() { cb() });
}
Chain([

    r=>{ 
        UserData('elle',  i=>{ 
            console.log(i);
            r(i)
        }) 
    },
    r=>{ 
        UserData('tyler', {age:"22"}, i=>{ 
            console.log(i);
            r(i);
        }) 
    },
    r=>{ 
        UserData('nerd',  i=>{ 
            console.log(i);
            r(i) 
        }) 
    },
    r=>{ 
        UserData('jeax',  i=>{ 
            console.log(i);
            r(i) 
        }) 
    }

], function(){

    console.log("Done.");

});

下面的示例按我的需要工作,只是没有使用我想要的链功能和每个项目的简单性。

var PChain = function(cb){
    // Convert each item.
    return function(){ return new Promise(r=>{cb(r)}) };
}
// The items.
var chain = [
    PChain(r=>{ 
        UserData('elle',  i=>{ 
            console.log(i);r(i)
        }) 
    }),
    PChain(r=>{ 
        UserData('tyler', {age:"22"}, i=>{ 
            console.log(i);r(i);
        }) 
    }),
    PChain(r=>{ 
        UserData('nerd',  
            i=>{ console.log(i);r(i) 
        }) 
    }),
    PChain(r=>{ 
        UserData('jeax',  
            i=>{ console.log(i);r(i) 
        }) 
    })
];
chain.reduce(function(cur, next) { return cur.then(next) }, Promise.resolve()).then(function() {
    //all executed
    console.log("Done.");
});

【问题讨论】:

  • UserData 看起来很奇怪:它不返回承诺,也不遵守(err, result) 参数的常规回调约定。
  • 虽然我收到了很多嘲笑,但我还是使用 e 作为第二个参数。在这种情况下,我没有使用它,因为我只是在测试。 UserData 使用 MongoDB 返回配置文件信息。
  • 从上面的代码中并不清楚为什么不应该并行处理它,但我们假设它确实不应该。我相信 Bluebird 可能会提供您需要的东西,mapSeries 处理一系列承诺数组。上面的代码不必要地复杂,我不能建议它在这里如何应用。

标签: javascript ecmascript-6


【解决方案1】:

你的错误在于Chain函数:

chain.forEach(function(i){  i = function(r){return new Promise(i)};   });
chain.reduce(function(cur, next) { {cur.then(next)} }, Promise.resolve()).then(function() { cb() });

具体来说,在forEach 中分配给i 绝对没有任何作用。你真正想要的是使用map:

chain.map(function(exec){ return function(r){ return new Promise(exec)}; }).reduce(…);

map 返回一个函数数组,然后您可以对其进行归约。


不管怎样,你实际上应该做的是promisifyUserData函数:

function user(name, options) {
    return new Promise((resolve, reject) => {
        if (options)
            UserData(name, options, resolve);
        else
            UserData(name, resolve);
    });
}

你甚至不需要链,但可以简单地写

user('elle').then(i => { 
    console.log(i);
    return user('tyler', {age:"22"});
}).then(i => { 
    console.log(i);
    return user('nerd');
}).then(i => {
    console.log(i);
    return user('jeax');
}).then(i => {
    console.log(i);
}).then(function() {
   //all executed
   console.log("Done.");
});

如果您有一个动态大小的用户名数组,您仍然可以使用reduce,但您不应该使用函数数组。

【讨论】:

  • 非常感谢!这是一个更好的选择,正是我正在寻找的 :-) +1 因为没有将我推荐给某个图书馆。
【解决方案2】:

你应该使用Promise.all(iterable)

Promise.all([r1, r2, r3]).then(function(values) { 
  // Do something
});

更新

要遵守特定顺序,您应该check this answer。不幸的是,没有 ES6 方法能够原生地做到这一点。

最常用的 JS 承诺队列系统之一(除其他外,by Angular)是 Kris Kowal 的 Q,它确实是 recommend something similar,所以这与您所写的相差不远。

注意

在您的代码中,您似乎正在尝试实现某种批处理请求。你要知道,在 JS 中真正需要一个有序的 Promise 链是非常稀缺的。

  1. 如果您将其用于流(如文件内容操作),最好使用pipes
  2. 如果您将其用于批处理/过滤 API 请求:
    • 如果您控制 API:更改您的 API 以管理批处理/过滤请求
    • 如果您无法控制它:创建一个中间件应用程序来管理它

【讨论】:

  • They need to be in order
  • 我编辑了我的答案以给出更具体的解释。
  • 不要显得粗鲁,但这不是一个答案,这是一个建议,我不是在练习。我是那些相信如果语言允许我这样做的人之一,那很好。我想要我想要的。继续前进,.all 一次运行它们,而不是按顺序运行。这就是为什么我试图用我的 forEach 语句修改链中的每个函数以正确链接它们。我创建了一个名为 PCChain 的函数,它的功能几乎相同且有效,我将更新我的问题以提供它。
  • @TylerJames 您是否阅读了答案的所有要点? UPDATE 部分为您提供了 2 个 ordered 承诺队列示例。
  • @TylerJames 说实话:如果你想按照自己的方式做事,那很好,但不要要求人们简化你的代码,并且期望他们提出很多建议更常见的技术。正如您已经注意到的,您的方法不是大多数人解决实际问题的方法。
猜你喜欢
  • 1970-01-01
  • 2015-10-06
  • 1970-01-01
  • 1970-01-01
  • 2012-07-17
  • 2015-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多