Ber*_*rgi 607 javascript scope promise bluebird es6-promise
我已经将我的代码重组为承诺,并构建了一个由多个回调组成的精彩长扁平承诺链.then().最后我想返回一些复合值,并且需要访问多个中间承诺结果.但是,序列中间的分辨率值不在最后一个回调的范围内,我该如何访问它们?
function getExample() {
return promiseA(…).then(function(resultA) {
// Some processing
return promiseB(…);
}).then(function(resultB) {
// More processing
return // How do I gain access to resultA here?
});
}
Run Code Online (Sandbox Code Playgroud)
Ber*_*rgi 358
当您需要访问链中的中间值时,您应该将链条拆分成您需要的那些单件.而不是附加一个回调并以某种方式尝试多次使用其参数,将多个回调附加到同一个承诺 - 无论您需要结果值.不要忘记,承诺只代表(代理)未来的价值!接下来,在线性链中从另一个派生一个承诺,使用库提供给您的promise组合器来构建结果值.
这将导致非常简单的控制流程,清晰的功能组合,因此易于模块化.
function getExample() {
var a = promiseA(…);
var b = a.then(function(resultA) {
// some processing
return promiseB(…);
});
return Promise.all([a, b]).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
Run Code Online (Sandbox Code Playgroud)
Promise.all在ES5中只有ES6可用的回调中的参数解构代替,在ES5中,then调用将被许多promise库(Q,Bluebird,when,...)提供的漂亮的辅助方法取代:.spread(function(resultA, resultB) { ….
Bluebird还具有专用join功能,可以用更简单(更高效)的构造替换Promise.all+ spread组合:
…
return Promise.join(a, b, function(resultA, resultB) { … });
Run Code Online (Sandbox Code Playgroud)
Ber*_*rgi 221
当然,这个问题也得到了语言设计者的认可.他们做了很多工作,异步功能提案终于成功了
您不再需要单个then调用或回调函数,因为在异步函数(在调用时返回一个promise)中,您只需等待promises直接解析即可.它还具有任意控制结构,如条件,循环和try-catch-clause,但为了方便起见,我们在这里不需要它们:
async function getExample() {
var resultA = await promiseA(…);
// some processing
var resultB = await promiseB(…);
// more processing
return // something using both resultA and resultB
}
Run Code Online (Sandbox Code Playgroud)
在我们等待ES8的同时,我们已经使用了非常类似的语法.ES6带有生成器功能,允许在任意放置的yield关键字中将执行分开.这些切片可以相互独立地运行,甚至是异步运行 - 而这正是我们在运行下一步之前等待promise解析时所做的事情.
有专门的库(比如co或task.js),但是许多promise库都有辅助函数(Q,Bluebird,when,...),当你给它们一个生成器函数时,它会为你执行这个async逐步执行产生承诺.
var getExample = Promise.coroutine(function* () {
// ^^^^^^^^^^^^^^^^^ Bluebird syntax
var resultA = yield promiseA(…);
// some processing
var resultB = yield promiseB(…);
// more processing
return // something using both resultA and resultB
});
Run Code Online (Sandbox Code Playgroud)
从版本4.0开始,这在Node.js中工作,也有一些浏览器(或他们的开发版)相对较早地支持生成器语法.
但是,如果您希望/需要向后兼容,则不能使用没有转换器的那些.当前工具支持生成器函数和异步函数,例如参见Babel on generator和async函数的文档.
然后,还有许多其他编译到JS的语言
专门用于简化异步编程.他们通常使用类似语法await(例如冰的CoffeeScript),但也有其他人配备了哈斯克尔样do-notation(如LatteJs,一元,PureScript或LispyScript).
Esa*_*ija 100
将promises-for-later-needed-values分配给变量,然后通过同步检查获取它们的值.该示例使用bluebird的.value()方法,但许多库提供类似的方法.
function getExample() {
var a = promiseA(…);
return a.then(function() {
// some processing
return promiseB(…);
}).then(function(resultB) {
// a is guaranteed to be fulfilled here so we can just retrieve its
// value synchronously
var aValue = a.value();
});
}
Run Code Online (Sandbox Code Playgroud)
这可以用于任意数量的值:
function getExample() {
var a = promiseA(…);
var b = a.then(function() {
return promiseB(…)
});
var c = b.then(function() {
return promiseC(…);
});
var d = c.then(function() {
return promiseD(…);
});
return d.then(function() {
return a.value() + b.value() + c.value() + d.value();
});
}
Run Code Online (Sandbox Code Playgroud)
Ber*_*rgi 53
使用闭包来维护变量的范围(在我们的例子中,成功的回调函数参数)是自然的JavaScript解决方案.有了promises,我们可以任意地嵌套和压缩 .then()回调 - 它们在语义上是等价的,除了内部的范围.
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(function(resultB) {
// more processing
return // something using both resultA and resultB;
});
});
}
Run Code Online (Sandbox Code Playgroud)
当然,这是建立一个缩进金字塔.如果缩进变得太大,你仍然可以使用旧工具来对抗厄运的金字塔:模块化,使用额外的命名函数,并在不再需要变量时立即压缩承诺链.
理论上,你总是可以避免两个以上的嵌套级别(通过使所有闭包显式化),在实践中使用尽可能多的合理.
function getExample() {
// preprocessing
return promiseA(…).then(makeAhandler(…));
}
function makeAhandler(…)
return function(resultA) {
// some processing
return promiseB(…).then(makeBhandler(resultA, …));
};
}
function makeBhandler(resultA, …) {
return function(resultB) {
// more processing
return // anything that uses the variables in scope
};
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用辅助功能对于这种局部的应用,如_.partial从下划线/lodash或本地.bind()方法,进一步减少缩进:
function getExample() {
// preprocessing
return promiseA(…).then(handlerA);
}
function handlerA(resultA) {
// some processing
return promiseB(…).then(handlerB.bind(null, resultA));
}
function handlerB(resultA, resultB) {
// more processing
return // anything that uses resultA and resultB
}
Run Code Online (Sandbox Code Playgroud)
Ber*_*rgi 49
与嵌套回调类似,此技术依赖于闭包.然而,链条保持平稳 - 而不是仅传递最新结果,每一步都会传递一些状态对象.这些状态对象累积先前操作的结果,再次传递稍后将需要的所有值以及当前任务的结果.
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
Run Code Online (Sandbox Code Playgroud)
这里,小箭头b => [resultA, b]是关闭的函数,resultA并将两个结果的数组传递给下一步.其中使用参数解构语法再次将其分解为单个变量.
在ES6进行解构之前.spread(),许多promise库(Q,Bluebird,when,......)提供了一个很好的辅助方法.它需要一个带有多个参数的函数 - 每个数组元素一个 - 用作.spread(function(resultA, resultB) { ….
当然,这里所需的闭包可以通过一些辅助函数进一步简化,例如
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));
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用Promise.all以生成数组的承诺:
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped
// as if passed to Promise.resolve()
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
Run Code Online (Sandbox Code Playgroud)
而且您可能不仅使用数组,而且使用任意复杂的对象.例如,使用_.extend或Object.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 processing
return promiseB(…).then(augment({resultA}, "resultB"));
}).then(function(obj) {
// more processing
return // something using both obj.resultA and obj.resultB
});
}
Run Code Online (Sandbox Code Playgroud)
虽然这种模式保证了扁平链条,但显式状态对象可以提高清晰度,但对于长链来说,这将变得乏味.特别是当您偶尔需要状态时,您仍然必须通过每一步.通过这个固定的接口,链中的单个回调相互紧密耦合,并且不灵活.它使得单个步骤的分解变得更加困难,并且不能直接从其他模块提供回调 - 它们总是需要包含在关注状态的样板代码中.像上面这样的抽象辅助函数可以缓解疼痛,但它总会存在.
Ber*_*rgi 35
简单(但不优雅且相当错误)的解决方案是只使用更高范围的变量(链中的所有回调都可以访问)并在获取它们时将结果值写入它们:
function getExample() {
var resultA;
return promiseA(…).then(function(_resultA) {
resultA = _resultA;
// some processing
return promiseB(…);
}).then(function(resultB) {
// more processing
return // something using both resultA and resultB
});
}
Run Code Online (Sandbox Code Playgroud)
可以使用(最初为空的)对象而不是许多变量,在该对象上将结果存储为动态创建的属性.
该解决方案有几个缺点:
Bluebird库鼓励使用传递的对象,使用它们的bind()方法将上下文对象分配给promise链.它可以通过不可用的this关键字从每个回调函数访问.虽然对象属性比变量更容易被检测到错别字,但模式非常聪明:
function getExample() {
return promiseA(…)
.bind({}) // Bluebird only!
.then(function(resultA) {
this.resultA = resultA;
// some processing
return promiseB(…);
}).then(function(resultB) {
// more processing
return // 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
}
Run Code Online (Sandbox Code Playgroud)
这种方法可以很容易地在不支持.bind的promise库中进行模拟(尽管以更冗长的方式并且不能在表达式中使用):
function getExample() {
var ctx = {};
return promiseA(…)
.then(function(resultA) {
this.resultA = resultA;
// some processing
return promiseB(…);
}.bind(ctx)).then(function(resultB) {
// more processing
return // something using both this.resultA and resultB
}.bind(ctx));
}
Run Code Online (Sandbox Code Playgroud)
Jay*_*Jay 14
使用本地范围的对象来收集承诺链中的中间结果是您提出的问题的合理方法.请考虑以下代码段:
function getExample(){
//locally scoped
const results = {};
return promiseA(paramsA).then(function(resultA){
results.a = resultA;
return promiseB(paramsB);
}).then(function(resultB){
results.b = resultB;
return promiseC(paramsC);
}).then(function(resultC){
//Resolve with composite of all promises
return Promise.resolve(results.a + results.b + resultC);
}).catch(function(error){
return Promise.reject(error);
});
}
Run Code Online (Sandbox Code Playgroud)
节点7.4现在支持带有和声标志的异步/等待调用.
试试这个:
async function getExample(){
let response = await returnPromise();
let response2 = await returnPromise2();
console.log(response, response2)
}
getExample()
Run Code Online (Sandbox Code Playgroud)
并运行该文件:
node --harmony-async-await getExample.js
简单就可以了!
这几天,我也遇到了一些像你这样的问题.最后,我找到了一个很好的解决方案,这个问题简单易读.我希望这可以帮到你.
根据how-to-chain-javascript-promises
好的,让我们来看看代码:
const firstPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('first promise is completed');
resolve({data: '123'});
}, 2000);
});
};
const secondPromise = (someStuff) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('second promise is completed');
resolve({newData: `${someStuff.data} some more data`});
}, 2000);
});
};
const thirdPromise = (someStuff) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('third promise is completed');
resolve({result: someStuff});
}, 2000);
});
};
firstPromise()
.then(secondPromise)
.then(thirdPromise)
.then(data => {
console.log(data);
});
Run Code Online (Sandbox Code Playgroud)
另一个答案,使用babel-node版本<6
运用 async - await
npm install -g babel@5.6.14
example.js:
async function getExample(){
let response = await returnPromise();
let response2 = await returnPromise2();
console.log(response, response2)
}
getExample()
Run Code Online (Sandbox Code Playgroud)
然后,跑babel-node example.js,瞧!
| 归档时间: |
|
| 查看次数: |
181214 次 |
| 最近记录: |