本文翻译自:How do I access previous promise results in a .then() chain?

I have restructured my code to promises , and built a wonderful long flat promise chain , consisting of multiple .then() callbacks. 我已经将我的代码重组为Promise ,并构建了一个精彩的,长而平坦的Promise链 ,其中包括多个.then()回调。 In the end I want to return some composite value, and need to access multiple intermediate promise results . 最后,我想返回一些复合值,并且需要访问多个中间promise结果 。 However the resolution values from the middle of the sequence are not in scope in the last callback, how do I access them? 但是,序列中间的分辨率值不在上次回调的范围内,如何访问它们?

function getExample() {return promiseA(…).then(function(resultA) {// Some processingreturn promiseB(…);}).then(function(resultB) {// More processingreturn // How do I gain access to resultA here?});
}

#1楼

参考:https://stackoom.com/question/1uXI8/如何访问-then-链中的先前的诺言结果


#2楼

Nesting (and) closures 嵌套(和)闭包

Using closures for maintaining the scope of variables (in our case, the success callback function parameters) is the natural JavaScript solution. 使用闭包来维护变量的范围(在我们的示例中为成功回调函数参数)是自然的JavaScript解决方案。 With promises, we can arbitrarily nest and flatten .then() callbacks - they are semantically equivalent, except for the scope of the inner one. 有了promise,我们可以随意嵌套和展平 .then()回调-它们在语义上是等效的,但内部回调的范围除外。

function getExample() {return promiseA(…).then(function(resultA) {// some processingreturn promiseB(…).then(function(resultB) {// more processingreturn // something using both resultA and resultB;});});
}

Of course, this is building an indentation pyramid. 当然,这是在构建压痕金字塔。 If indentation is getting too large, you still can apply the old tools to counter the pyramid of doom : modularize, use extra named functions, and flatten the promise chain as soon as you don't need a variable any more. 如果缩进变得太大,您仍然可以使用旧工具来反击厄运金字塔 :模块化,使用额外的命名函数,并在不再需要变量时立即平整承诺链。
In theory, you can always avoid more than two levels of nesting (by making all closures explicit), in practise use as many as are reasonable. 从理论上讲,您总是可以避免两个以上的嵌套级别(通过使所有闭包都明确),实际上可以使用尽可能多的嵌套。

function getExample() {// preprocessingreturn promiseA(…).then(makeAhandler(…));
}
function makeAhandler(…)return function(resultA) {// some processingreturn promiseB(…).then(makeBhandler(resultA, …));};
}
function makeBhandler(resultA, …) {return function(resultB) {// more processingreturn // anything that uses the variables in scope};
}

You can also use helper functions for this kind of partial application , like _.partial from Underscore / lodash or the native .bind() method , to further decrease indentation: 您还可以使用辅助功能对于这种局部的应用 ,像_.partial从下划线 / lodash或本地.bind()方法 ,以进一步降低缩进:

function getExample() {// preprocessingreturn promiseA(…).then(handlerA);
}
function handlerA(resultA) {// some processingreturn promiseB(…).then(handlerB.bind(null, resultA));
}
function handlerB(resultA, resultB) {// more processingreturn // anything that uses resultA and resultB
}

#3楼

Explicit pass-through 显式传递

Similar to nesting the callbacks, this technique relies on closures. 与嵌套回调类似,此技术依赖于闭包。 Yet, the chain stays flat - instead of passing only the latest result, some state object is passed for every step. 但是,链条保持不变-不仅传递最新结果,还为每个步骤传递了一个状态对象。 These state objects accumulate the results of the previous actions, handing down all values that will be needed later again plus the result of the current task. 这些状态对象会累积先前操作的结果,并传回以后将再次需要的所有值以及当前任务的结果。

function getExample() {return promiseA(…).then(function(resultA) {// some processingreturn promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }}).then(function([resultA, resultB]) {// more processingreturn // something using both resultA and resultB});
}

Here, that little arrow b => [resultA, b] is the function that closes over resultA , and passes an array of both results to the next step. 在这里,那个小箭头b => [resultA, b]是关闭resultA的函数,并将两个结果的数组传递给下一步。 Which uses parameter destructuring syntax to break it up in single variables again. 它使用参数解构语法将其再次分解为单个变量。

Before destructuring became available with ES6, a nifty helper method called .spread() was provided by many promise libraries ( Q , Bluebird , when , …). 在ES6进行销毁之前,许多promise库( Q , Bluebird , when …)都提供了一个名为.spread()的漂亮辅助方法。 It takes a function with multiple parameters - one for each array element - to be used as .spread(function(resultA, resultB) { … . 它需要一个具有多个参数的函数-每个数组元素一个-用作.spread(function(resultA, resultB) { …

Of course, that closure needed here can be further simplified by some helper functions, eg 当然,此处所需的关闭可以通过一些辅助功能进一步简化,例如

function addTo(x) {// imagine complex `arguments` fiddling or anything that helps usability// but you get the idea with this simple one:return res => [x, res];
}…
return promiseB(…).then(addTo(resultA));

Alternatively, you can employ Promise.all to produce the promise for the array: 或者,您可以使用Promise.all为数组生成promise:

function getExample() {return promiseA(…).then(function(resultA) {// some processingreturn Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped// as if passed to Promise.resolve()}).then(function([resultA, resultB]) {// more processingreturn // something using both resultA and resultB});
}

And you might not only use arrays, but arbitrarily complex objects. 您不仅可以使用数组,还可以使用任意复杂的对象。 For example, with _.extend or Object.assign in a different helper function: 例如,使用_.extendObject.assign在另一个帮助器函数中:

function augment(obj, name) {return function (res) { var r = Object.assign({}, obj); r[name] = res; return r; };
}function getExample() {return promiseA(…).then(function(resultA) {// some processingreturn promiseB(…).then(augment({resultA}, "resultB"));}).then(function(obj) {// more processingreturn // something using both obj.resultA and obj.resultB});
}

While this pattern guarantees a flat chain and explicit state objects can improve clarity, it will become tedious for a long chain. 尽管此模式可以保证链条平坦,并且显式状态对象可以提高清晰度,但对于长链而言,这将变得乏味。 Especially when you need the state only sporadically, you still have to pass it through every step. 尤其是当您仅偶尔需要状态时,您仍然必须将其传递给每一步。 With this fixed interface, the single callbacks in the chain are rather tightly coupled and inflexible to change. 有了这个固定的接口,链中的单个回调就紧密地耦合在一起,并且不易更改。 It makes factoring out single steps harder, and callbacks cannot be supplied directly from other modules - they always need to be wrapped in boilerplate code that cares about the state. 这使得分解单个步骤变得更加困难,并且回调不能直接从其他模块提供-始终需要将它们包装在关心状态的样板代码中。 Abstract helper functions like the above can ease the pain a bit, but it will always be present. 像上面这样的抽象辅助函数可以稍微减轻痛苦,但是它总是存在的。


#4楼

ECMAScript Harmony ECMAScript和声

Of course, this problem was recognized by the language designers as well. 当然,语言设计者也意识到了这个问题。 They did a lot of work and the async functions proposal finally made it into 他们做了很多工作, 异步功能提案最终使它成为了

ECMAScript 8 ECMAScript 8

You don't need a single then invocation or callback function any more, as in an asynchronous function (that returns a promise when being called) you can simply wait for promises to resolve directly. 您不再需要单个then调用或回调的函数,因为在异步函数(被调用时返回一个Promise)中,您可以简单地等待Promise直接解析。 It also features arbitrary control structures like conditions, loops and try-catch-clauses, but for the sake of convenience we don't need them here: 它还具有诸如条件,循环和try-catch-clauses之类的任意控制结构,但是为了方便起见,我们在这里不需要它们:

async function getExample() {var resultA = await promiseA(…);// some processingvar resultB = await promiseB(…);// more processingreturn // something using both resultA and resultB
}

ECMAScript 6 ECMAScript 6

While we were waiting for ES8, we already did use a very similar kind of syntax. 在等待ES8时,我们确实使用了非常相似的语法。 ES6 came with generator functions , which allow to break the execution apart in pieces at arbitrarily placed yield keywords. ES6带有生成器函数 ,该函数允许在任意放置的yield关键字处将执行分段。 Those slices can be run after each other, independently, even asynchronously - and that's just what we do when we want to wait for a promise resolution before running the next step. 这些切片可以相互独立,甚至异步地运行-这就是我们要在运行下一步之前等待promise解析时所要做的。

There are dedicated libraries (like co or task.js ), but also many promise libraries have helper functions ( Q , Bluebird , when , …) that do this async step-by-step execution for you when you give them a generator function that yields promises. 有专用的库(例如co或task.js ),但是还有许多Promise库具有辅助函数( Q , Bluebird , when …),当您为它们提供生成器函数时,它们会为您逐步执行异步操作产生希望。

var getExample = Promise.coroutine(function* () {
//               ^^^^^^^^^^^^^^^^^ Bluebird syntaxvar resultA = yield promiseA(…);// some processingvar resultB = yield promiseB(…);// more processingreturn // something using both resultA and resultB
});

This did work in Node.js since version 4.0, also a few browsers (or their dev editions) did support generator syntax relatively early. 从4.0版本开始,它确实在Node.js中起作用,而且一些浏览器(或其开发版本)相对较早地支持生成器语法。

ECMAScript 5 ECMAScript 5

However, if you want/need to be backwards-compatible you cannot use those without a transpiler. 但是,如果您希望/需要向后兼容,那么在没有编译器的情况下不能使用它们。 Both generator functions and async functions are supported by the current tooling, see for example the documentation of Babel on generators and async functions . 当前工具支持生成器功能和异步功能,例如,请参见Babel有关生成器和异步功能的文档。

And then, there are also many other compile-to-JS languages that are dedicated to easing asynchronous programming. 然后,还有许多其他专用于JS的可编译为JS的语言致力于简化异步编程。 They usually use a syntax similar to await , (eg Iced CoffeeScript ), but there are also others that feature a Haskell-like do -notation (eg LatteJs , monadic , PureScript or LispyScript ). 他们通常使用类似语法await (例如冰的CoffeeScript ),但也有其他人配备了哈斯克尔样do -notation(如LatteJs , 一元 , PureScript或LispyScript )。


#5楼

Mutable contextual state 可变的上下文状态

The trivial (but inelegant and rather errorprone) solution is to just use higher-scope variables (to which all callbacks in the chain have access) and write result values to them when you get them: 一个简单的(但不那么优雅,而且容易出错)的解决方案是只使用更高范围的变量(链中所有回调都可以访问),并在获得它们时将结果值写入它们:

function getExample() {var resultA;return promiseA(…).then(function(_resultA) {resultA = _resultA;// some processingreturn promiseB(…);}).then(function(resultB) {// more processingreturn // something using both resultA and resultB});
}

Instead of many variables one might also use an (initially empty) object, on which the results are stored as dynamically created properties. 除了使用许多变量之外,还可以使用一个(最初为空)对象,其结果存储为动态创建的属性。

This solution has several drawbacks: 该解决方案有几个缺点:

  • Mutable state is ugly , and global variables are evil . 可变状态是丑陋的 , 全局变量是邪恶的 。
  • This pattern doesn't work across function boundaries, modularising the functions is harder as their declarations must not leave the shared scope 这种模式无法跨函数边界工作,对函数进行模块化更加困难,因为它们的声明不能离开共享范围
  • The scope of the variables does not prevent to access them before they are initialized. 变量的范围不会阻止对其进行初始化之前对其的访问。 This is especially likely for complex promise constructions (loops, branching, excptions) where race conditions might happen. 对于竞争条件可能发生的复杂的承诺构造(循环,分支,排他),这尤其可能。 Passing state explicitly, a declarative design that promises encourage, forces a cleaner coding style which can prevent this. 显式地通过状态,承诺鼓励的声明性设计迫使采用更干净的编码样式,可以防止这种情况的发生。
  • One must choose the scope for those shared variables correctly. 必须正确选择这些共享变量的范围。 It needs to be local to the executed function to prevent race conditions between multiple parallel invocations, as would be the case if, for example, state was stored on an instance. 它必须在已执行函数的本地,以防止多个并行调用之间的竞争情况,例如,例如,将状态存储在实例中时就是这种情况。

The Bluebird library encourages the use of an object that is passed along, using their bind() method to assign a context object to a promise chain. 蓝鸟库鼓励使用传递的对象,并使用其bind()方法将上下文对象分配给承诺链。 It will be accessible from each callback function via the otherwise unusable this keyword . 每个回调函数都可以通过否则无法使用的this关键字进行访问 。 While object properties are more prone to undetected typos than variables, the pattern is quite clever: 尽管对象属性比变量更容易出现错别字,但这种模式非常聪明:

function getExample() {return promiseA(…).bind({}) // Bluebird only!.then(function(resultA) {this.resultA = resultA;// some processingreturn promiseB(…);}).then(function(resultB) {// more processingreturn // something using both this.resultA and resultB}).bind(); // don't forget to unbind the object if you don't want the// caller to access it
}

This approach can be easily simulated in promise libraries that do not support .bind (although in a somewhat more verbose way and cannot be used in an expression): 可以在不支持.bind的promise库中轻松模拟此方法(尽管有些冗长,并且不能在表达式中使用):

function getExample() {var ctx = {};return promiseA(…).then(function(resultA) {this.resultA = resultA;// some processingreturn promiseB(…);}.bind(ctx)).then(function(resultB) {// more processingreturn // something using both this.resultA and resultB}.bind(ctx));
}

#6楼

Break the chain 断链

When you need to access the intermediate values in your chain, you should split your chain apart in those single pieces that you need. 当需要访问链中的中间值时,应将链分成所需的单个部分。 Instead of attaching one callback and somehow trying to use its parameter multiple times, attach multiple callbacks to the same promise - wherever you need the result value. 不必附加一个回调并以某种方式尝试多次使用其参数,而是将多个回调附加到同一promise-无论何时需要结果值。 Don't forget, a promise just represents (proxies) a future value ! 不要忘记, 承诺只是代表(代理)未来的价值 ! Next to deriving one promise from the other in a linear chain, use the promise combinators that are given to you by your library to build the result value. 除了从线性链中的另一个承诺中推导一个承诺之外,还使用库提供给您的承诺组合器来构建结果值。

This will result in a very straightforward control flow, clear composition of functionalities and therefore easy modularisation. 这将导致非常简单的控制流程,清晰的功能组成,因此易于模块化。

function getExample() {var a = promiseA(…);var b = a.then(function(resultA) {// some processingreturn promiseB(…);});return Promise.all([a, b]).then(function([resultA, resultB]) {// more processingreturn // something using both resultA and resultB});
}

Instead of the parameter destructuring in the callback after Promise.all that only became available with ES6, in ES5 the then call would be replaced by a nifty helper method that was provided by many promise libraries ( Q , Bluebird , when , …): .spread(function(resultA, resultB) { … . 与其在Promise.all之后的回调中进行参数解构(仅在ES6中可用),在ES5中, then调用将由许多promise库( Q , Bluebird , when ,…)提供的漂亮的助手方法代替: .spread(function(resultA, resultB) { …

Bluebird also features a dedicated join function to replace that Promise.all + spread combination with a simpler (and more efficient) construct: 蓝鸟还具有专用的join功能,可以用更简单(更高效)的结构替换Promise.all + spread组合:

…
return Promise.join(a, b, function(resultA, resultB) { … });

如何访问.then()链中的先前的诺言结果?相关推荐

  1. 区块链中密码学与安全技术

    区块链的定义 区块链的定义,应当是:区块链是一种按照时间顺序将数据进行分布式存储的块链式数据结构,它利用共识机制进行数据验证,利用密码学进行数据保护和用户安全访问,利用智能合约来操作数据,从而成为不可 ...

  2. 人工智能在音频链中找到自己的声音

    人工智能在音频链中找到自己的声音 AI finds its voice in audio chain 硅基silicon-based麦克风的出现重塑了音频领域.但在未来几年,市场研究公司Yole Dé ...

  3. 在公共区块链中通过加密保护数据

    链客,专为开发者而生,有问必答! 此文章来自链客区块链技术问答社区,未经允许拒绝转载. 隐私限制 在处理或交换业务文件时,贸易伙伴可能需要某些隐私因素. (1)交易数据的隐私性: 交易数据仅供交易双方 ...

  4. 域用户更改密码提示拒绝访问_AD域中的ACL攻防探索

    前言关于域内ACL的攻防近两年经常被人所提起,同时也产生了很多关于域内ACL相关的工具和攻击方式,本文将会从ACL的概念谈起,然后介绍几种不同的域内攻击方式以及如何监测和防御对于ACL的攻击.ACL的 ...

  5. 区块链中的密码学,使用ABE结合区块链

    ABE 密码学,以及与区块链结合的价值 背景 区块链技术具备篡改难度高.使用成本低.分布式的优点,本应成为各行各业的重要助力.但是由于链上参与方担心自己的核心数据外泄,不愿将自己的核心数据上链,这个原 ...

  6. gossip 区块链_区块链中的P2P

    区块链中P2P介绍 p2p是什么 为什么区块链需要P2P 比特币.以太坊.超级账本和EOS的P2P对比 P2P是什么 P2P作为区块链网络中去中心化的标识 P2P全称对等式网络(peer-to-pee ...

  7. 微软、苹果、谷歌、三星……这些区块链中的科技巨头原来已经做了这么多事!...

    作者 | Gareth Jenkinson 译者 | 火火酱 责编 | 徐威龙 出品 | 区块链大本营(blockchain_camp) 在过去几年中,全球最大的几家科技公司一直在共同努力,研究和集成 ...

  8. 联盟链中的Hyperledger Fabric、FISCO BCOS和CITA

    本文援引自链接:https://mp.weixin.qq.com/s/4JAZGwI32bZNlxBqPfkihg 一.摘要 第 46 届世界经济论坛达沃斯年会将区块链与人工智能.自动驾驶等一并列入& ...

  9. 快速识别区块链中的骗局

    2021年的区块链市场呈现多元化发展态势,有国家层面对于区块链的支持和推动,也有国际机构和知名企业的支持,还有NFT市场.跨链等生态的爆发:这些都吸引了足够的眼球,使更多人踊跃的参与到了区块链行业. ...

最新文章

  1. 第十三届全国大学生智能汽车竞赛获奖名单
  2. Jmeter使用之:高效组织接口自动化用例技巧
  3. du,df,fdisk,mkfs.ext3命令详解
  4. 让你的原型不再LOW-一套设计师倾情奉献的组件库免费下载
  5. Marketing Cloud的语音输入功能
  6. 计算机投诉信英语作文,投诉信A Letter of Complaint
  7. win10 dns异常上不了网如何解决
  8. Servlet方法详解
  9. html5 制作会转的风扇,HTML5学习第5天[乱撞的球]可以听到风扇声的哟
  10. 程序员得到的报酬与他们的生产力不成正比
  11. 省城两日游,凉透了。。。
  12. SW2017学习笔记(一)基本的工作界面及鼠标操作
  13. 安装sqlserver 2017安装 需要安装oracle JRE7 更新 51(64位)或更高版本(已解决)
  14. 【老生谈算法】matlab实现图像复原算法源码——图像复原
  15. electron 剪贴板 截图_利用 Electron 开发快速截图工具(二)
  16. 软件测试非功能性需求,什么是非功能测试?非功能测试包括哪些类型?
  17. OKR目标管理专题及模板大全
  18. Python3实现HTTP请求
  19. 李小铭计算机专业应聘书作文,应聘申请书英语作文
  20. ggplot作图显示中文

热门文章

  1. Python-模块和包.深入Celery之子任务及原语组式/链式/回调
  2. java实现栈的数据结构
  3. [Servlet] 初识Servlet
  4. CSS3和js炫酷点击按钮3D翻转动画特效
  5. 兼容浏览器的insertAdjacentHTML
  6. html5 Canvas画图3:1像素线条模糊问题
  7. 挑战malloc极限,看看你的系统有多大的内存分配能力
  8. Python3.x:pip install pymssql安装时出错
  9. Python - 列表解析式/生成器表达式
  10. [ An Ac a Day ^_^ ] CodeForces 680A Bear and Five Cards