如何在没有“缩进金字塔”的情况下正确表达任意 Promise 链? [复制]
Posted
技术标签:
【中文标题】如何在没有“缩进金字塔”的情况下正确表达任意 Promise 链? [复制]【英文标题】:How to correctly express arbitrary Promise chains without "indentation pyramids"? [duplicate] 【发布时间】:2017-05-31 15:33:26 【问题描述】:有诸如Q.reduce
和Q.all
之类的方法有助于在异类promise 集合的特定情况下扁平化promise 链。不过,请注意一般情况:
const F = (x) => x;
const a = F(1);
const b = F(2);
const c = F(a + b);
const d = F(a + c);
const e = F(b + c);
console.log(e);
也就是说,每个术语都依赖于先前定义的任意术语的一系列赋值。假设F
是一个异步调用:
const F = (x) => Q.delay(1000).return(x);
如果不生成缩进金字塔,我无法表达这种模式:
F(100).then(a =>
F(200).then(b =>
F(a+b).then(c =>
F(a+c).then(d =>
F(b+c).then(e =>
F(d+e).then(f =>
console.log(f)
)
)
)
)
)
);
请注意,使用返回值是行不通的:
F(100).then(a => F(200))
.then(b => F(a+b))
.then(c => F(a+c))
.then(d => F(b+c))
.then(e => F(d+e))
.then(f => console.log(f));
例如,因为a
不在第二行的范围内。处理这种情况的正确方法是什么?
【问题讨论】:
这里有各种方法:How to chain and share prior results with promises。事实上,这个问题可能是那个问题的重复。 @jfriend00:绝对是同一个副本的副本,很好的发现。 绝对是重复的,我明白了。不过,我认为这个线程总体上更具启发性。 【参考方案1】:由于后续操作依赖于先前操作的多个位的方式,您的选择是:
做你做过的事
将变量放在链外,随时赋值
让整个事物在带有a
、b
等属性的对象周围传递
#1 是我会坚持的,除非有一个 真正 充分的理由去做其他两个中的任何一个。幸运的是,这种积累很少像您的问题中显示的那么深。
async
/await
是几年后的方式(可能是在发布问题时,the proposal 已完成,它们可以与转译器一起使用),请参阅Sterling's answer他们如何简化它。这是一个工作示例:
const F = x =>
return new Promise(resolve =>
setTimeout(() =>
resolve(x);
, 100);
);
;
(async () =>
const a = await F(100);
const b = await F(200);
const c = await F(a+b);
const d = await F(a+c);
const e = await F(b+c);
const f = await F(d+e);
console.log(f);
)()
.catch(error =>
// ...handle/report error...
);
Live on Babel's REPL 适用于环境过时的用户。
【讨论】:
async/await 可以帮助确定范围,或者您可以使用 Promise.all() iirc 使链接参数更容易。 (我已经为节点使用了异步库,它使传递参数变得容易) @SterlingArcher:async
/await
是一个非常的好点(如果你正在编译,你现在可以使用它)。
发布了一个更好的答案哈哈,我们可以感谢 JS 聊天中的 Luggage 提出的这个想法【参考方案2】:
第二次尝试。 JS 聊天中的@Luggage 建议使用 Async/Await 来保持参数的范围。
let a = await F(200);
let b = await F(a + 100);
let c = await F(a + b);
//... etc, all inside an async function
您也可以使用Promise.all
或(这是我的经验)我使用async
库来帮助解决这些问题。
async.waterfall([
(callback) =>
let a = F(200);
callback(null, a)
, (a, callback) =>
let b = F(a+b);
callback(null, b);
, //etc down the chain
]);
我认为Promise.all
会比异步库更好地管理它,但是 async/await 是这里最漂亮的方法,尽管它需要 ES2017 支持/转译。
【讨论】:
【参考方案3】:我认为 Async/await 很好地解决了这个问题;
async function runAsync()
const F = async (x) => x;
const a = await F(1);
const b = await F(2);
const c = await F(a + b);
const d = await F(a + c);
const e = await F(b + c);
console.log(e);
function runSync()
const F = (x) => x;
const a = F(1);
const b = F(2);
const c = F(a + b);
const d = F(a + c);
const e = F(b + c);
console.log(e);
runSync(); //5
runAsync(); //5
这将使用 node --harmony-async-await example
在节点 7 上本地运行
不幸的是,您可能需要进行转换以供一般使用,并且输出可能会变得非常大。
【讨论】:
有 nodejs async/await 模块,它做得很好以上是关于如何在没有“缩进金字塔”的情况下正确表达任意 Promise 链? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何修复“表达式类型'@lvalue CGRect/CGSize'在没有更多上下文的情况下模棱两可”?
如何正确使用case when表达式 和 decode函数?
在Java中,如何在没有正则表达式的情况下查找字符串中的第一个字符是不是为大写