假设您必须链接返回promises的函数,其中每个函数都需要某些(不一定是最后一个)其他承诺返回的值。
这种模式是否有名称,是否可以与承诺一起干净利落?
例如:
return getA().then(function (a) {
getB(a).then(function (b) {
getC(a, b).then (function (c) {
return {
a : a,
b : b,
c : c
}
});
});
});
你不能“天真地”压扁这个:
getA().then(function (a) {
return getB(a);
}).then(function (b) {
// Obviously won't work since a is not there any more
return getC(a, b);
}).then(function (c) {
// Same game, you can't access a or b
});
我能看到的唯一选择是getA,getB和getC实际上返回一个Promise,它使用包含a,然后是b,然后是c的对象来解析。 (因此每个函数都会构建它是最终结果的一部分。)
getA().then (function (result) {
return getB(result.a);
}).then(function (result) {
return getC(result.a, result.b);
}).then(function (result) {
// Last function is useless, just here to illustrate.
return result;
});
有没有办法在我的第一个例子中展平代码?
答案 0 :(得分:4)
使用promises的原因是为了避免嵌套回调。你的第一个例子使得promise链回归到一堆互相嵌套的回调函数中。 (不要这样做)
链可以这样使用:
var first, second, third;
getA().then(function (a) {
first = a;
return getB(first);
}).then(function (b) {
second = b;
return getC(first, second);
}).then(function (c) {
third = c;
return [first, second, third]; // have everything now!
});
你也应该再看一下你的代码。一个理想的承诺链应该是这样的:
getA().then(getB).then(getC).then(function(result) {
// done!
});
(当然,除非这些功能来自图书馆。)
答案 1 :(得分:4)
以下是您实际不需要的内容:
这里的诀窍是使用promises作为值的代理,它可以让你在行之后执行此行,就像使用其他顺序代码一样。这是“原生扁平化”:
var a = getA();
var b = a.then(getB);
var c = Promise.all([a,b]).spread(getC); // .all with a lambda if no .spread
现在假设您要访问a
b
或c
:
// .then and unwrap arguments if no spread, in Bluebird it's even better with join
Promise.all([a,b,c]).spread(function(a, b, c){
//a b and c are available here, note how we didn't need to nest
});
答案 2 :(得分:2)
是的,使用"累加器模式"。
写getA
,getB
和getC
来接受一个对象,对其进行扩充,并返回它或使用它解决的新承诺。
例如:
function getA(obj) {
// ...
// read/augment obj
// ...
return obj; // or return Promise.resolve(obj);
}
现在,调用表达式可以是超平链:
function foo() {
var obj = {
//initial properties
};
return Promise.resolve(obj).then(getA).then(getB).then(getC);
}
因此obj
是一个传递给链的令牌。任何处理器函数obj
/ getA
/ getB
对getC
所执行的任何操作都可由后续的所有处理器函数观察到。
显然,所有处理器功能(可能是链中的最后一个)必须遵守规则:
出于这个原因,"累加器模式"不能直接用于任何旧的第三方函数,尽管这些函数的自适应包装应该很容易编写。
<强> DEMO 强>
答案 3 :(得分:0)
如果您的承诺库支持.all()
(或者您正在使用支持.all()
的ES6标准承诺),您应该可以执行以下操作:
getA().then(function (a) {
return Promise.all([a, getB(a)]);
}).then(function (ab) {
return Promise.all(ab.concat(getC(ab[0], ab[1])));
}).then(function (abc) {
return { a : abc[0], b : abc[1], c : abc[2] };
});
另一方面,tcooc建议事先声明变量以保持结果非常简单,绝对值得考虑。