使用可读的函数名称扁平化承诺链
Flattening promise chain with readable function name
我在 Handling multiple catches in promise chain 中看到了 promise 实现,它产生了一个非常可读的链
return validateInput
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
但是,为了做到这一点,每个函数都需要 return 一个值而不是 Promise?由于 Promise 可以解析为值或 Promise,所以这不是问题。我的目标是让每个函数都具有可读的清晰逻辑。
尝试展开嵌套的 promise 函数时出现问题
return validateInput
.then(function(resultA) {
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
想象一下原始实现涉及访问先前承诺的值。有了嵌套的承诺,它很容易实现。但是对于展平链,我需要像这样分解每个函数
function validateInput = function (resultA ) {
return Promise.resolve({resultA : resultA, resultB :
}
function checkLoginPermission = function (mix ) {
let resultA = mix.resultA;
let resultB = mix.resultB
//Do something with resultA
...
}
当链中的最后一个函数从一开始就依赖某些东西时,情况会更糟。这意味着即使未使用该值,也必须从链的开头向下传递。
所以我是不是不小心踩到了某种可能影响性能的反模式?没有所有这些麻烦,我还能如何实现良好的可读性?
这实际上是 async
和 await
发挥作用的地方。当您需要跨多个异步 calls/promises 的结果在范围内时,这很好。如果你能用那个,我会说试试。
async function foo () {
const input = await validateInput()
const hasPermission = await checkLoginPermission(input)
const result = await checkDisableUser(hasPermission)
return await changePassword(result)
}
只需将变量传递给需要的函数即可。只是在那里展示一个例子。我也有点不确定您如何设置 validateInput,我认为您需要将 await
放在函数调用本身的前面。
如果您不能使用 async/await,我通常使用您的第二个代码片段,或者在顶部定义更高范围的变量:
let resultA
return validateInput
.then(function(result) {
resultA = result
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Promises 是一种模式,与函数式编程有关,直接将数据从一个函数传递到另一个函数是最基本的(称为 compose
,这里示例:http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/)。所以这绝不是反模式。
我看不出这种模式有什么问题。您可以将任何您想要的数据传递给下一个 Promise,并在嵌套的 Promises 中获取它们需要的数据。它非常透明和清晰:
function validateInput() {
return Promise.resolve({resultA: 1});
}
function checkLoginPermission(result) {
return new Promise(function(resolve, reject) {
// ...
// code
// ...
result.resultB = 2;
return resolve(result);
});
}
function checkDisableUser(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous function
let resultB = result.resultB;
// ...
// code
// ...
result.resultC = 3;
return resolve(result);
});
}
function changePassword(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous functions
let resultB = result.resultB;
let resultC = result.resultC;
// ...
// code
// ...
result.resultD = resultB * resultC;
return resolve(result);
});
}
validateInput()
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
您还可以在一些变量中收集数据,在 Promises 之前声明,这样您就不必传递结果。但它会破坏 Promise 的功能性。
内部 .then(/* ... */)
回调可以 return 原始值或解析为某个值的 Promise。如果它是另一个承诺,那么下一个 .then 将不会开始,直到内部承诺得到解决。本质上,Promise 总是解析为非 promise 类型。如果你 resolve 或 return 另一个 Promise,它将自动展开。
我想提出一个使用 ramda.js#pipeP() 的解决方案。
这个函数的好处是它按顺序解析承诺。
我们可以使用 pipeP()
:
重写您的示例
import pipeP from 'ramda/src/pipeP'
pipeP([
checkLoginPermission,
checkDisableUser,
changePassword
])(initialValue)
.then(responseChangePassword => { ... })
前一个 promise 的结果传递给下一个。
我在 Handling multiple catches in promise chain 中看到了 promise 实现,它产生了一个非常可读的链
return validateInput
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
但是,为了做到这一点,每个函数都需要 return 一个值而不是 Promise?由于 Promise 可以解析为值或 Promise,所以这不是问题。我的目标是让每个函数都具有可读的清晰逻辑。
尝试展开嵌套的 promise 函数时出现问题
return validateInput
.then(function(resultA) {
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
想象一下原始实现涉及访问先前承诺的值。有了嵌套的承诺,它很容易实现。但是对于展平链,我需要像这样分解每个函数
function validateInput = function (resultA ) {
return Promise.resolve({resultA : resultA, resultB :
}
function checkLoginPermission = function (mix ) {
let resultA = mix.resultA;
let resultB = mix.resultB
//Do something with resultA
...
}
当链中的最后一个函数从一开始就依赖某些东西时,情况会更糟。这意味着即使未使用该值,也必须从链的开头向下传递。
所以我是不是不小心踩到了某种可能影响性能的反模式?没有所有这些麻烦,我还能如何实现良好的可读性?
这实际上是 async
和 await
发挥作用的地方。当您需要跨多个异步 calls/promises 的结果在范围内时,这很好。如果你能用那个,我会说试试。
async function foo () {
const input = await validateInput()
const hasPermission = await checkLoginPermission(input)
const result = await checkDisableUser(hasPermission)
return await changePassword(result)
}
只需将变量传递给需要的函数即可。只是在那里展示一个例子。我也有点不确定您如何设置 validateInput,我认为您需要将 await
放在函数调用本身的前面。
如果您不能使用 async/await,我通常使用您的第二个代码片段,或者在顶部定义更高范围的变量:
let resultA
return validateInput
.then(function(result) {
resultA = result
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Promises 是一种模式,与函数式编程有关,直接将数据从一个函数传递到另一个函数是最基本的(称为 compose
,这里示例:http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/)。所以这绝不是反模式。
我看不出这种模式有什么问题。您可以将任何您想要的数据传递给下一个 Promise,并在嵌套的 Promises 中获取它们需要的数据。它非常透明和清晰:
function validateInput() {
return Promise.resolve({resultA: 1});
}
function checkLoginPermission(result) {
return new Promise(function(resolve, reject) {
// ...
// code
// ...
result.resultB = 2;
return resolve(result);
});
}
function checkDisableUser(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous function
let resultB = result.resultB;
// ...
// code
// ...
result.resultC = 3;
return resolve(result);
});
}
function changePassword(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous functions
let resultB = result.resultB;
let resultC = result.resultC;
// ...
// code
// ...
result.resultD = resultB * resultC;
return resolve(result);
});
}
validateInput()
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
您还可以在一些变量中收集数据,在 Promises 之前声明,这样您就不必传递结果。但它会破坏 Promise 的功能性。
内部 .then(/* ... */)
回调可以 return 原始值或解析为某个值的 Promise。如果它是另一个承诺,那么下一个 .then 将不会开始,直到内部承诺得到解决。本质上,Promise 总是解析为非 promise 类型。如果你 resolve 或 return 另一个 Promise,它将自动展开。
我想提出一个使用 ramda.js#pipeP() 的解决方案。
这个函数的好处是它按顺序解析承诺。
我们可以使用 pipeP()
:
import pipeP from 'ramda/src/pipeP'
pipeP([
checkLoginPermission,
checkDisableUser,
changePassword
])(initialValue)
.then(responseChangePassword => { ... })
前一个 promise 的结果传递给下一个。