Javascript 中可选异步预处理的成语是什么?
What is the idiom for optional asynchronous preprocessing in Javascript?
假设我必须做一些计算,例如包括计算 A(异步,带回调),然后在计算 A 时,计算 B(也是异步带回调)。
问题是A是可选的,如果没有数据,我应该直接跳到计算B。
目前我使用非常难看的代码和双倍的 B 执行,像这样:
if (A)
computeA(A,() => computeB(B,() => console.log("done.")));
else
computeB(B,() => console.log("done."));
我要求避免重复我的一些解决方案,任何 JS 开发人员都无法识别这些解决方案:-)是否有一些惯用语?
这本质上是延续传球风格。因为延续只是一个函数,所以您可以将总是发生的链分配给一个 var,然后在 if
的每个分支中重用它
let next = () => computeB(B,() => console.log("done."))
if (A)
computeA(A, next)
else
next()
一个微小的延续 monad
在 JavaScript 中使用延续真的很有趣,它们以非常有意义的方式表达特定问题。
这是连续 monad cont
的一个小实现,它允许您定义您希望在计算中链接的各种函数。
when
是这里的关键函数,因为它允许您指定一个有条件地绑定链中另一个函数的谓词。
下面,我们将从 0
的延续开始,(cont(0)
) 然后仅当当前延续的值小于 3
时才添加 1
– 后续对 when (lt (3)) (f)
的调用将 return 未更改值的延续
// cont :: a -> cont a
const cont = x =>
k => k (x)
// when :: (a -> Bool) -> (a -> cont a) -> a -> cont a
const when = p => f => x =>
p (x) ? f (x) : cont (x)
// contAdd :: Number -> Number -> cont Number
const contAdd = x => y =>
cont (x + y)
// lt :: Number -> Number -> Bool
const lt = x => y =>
y < x
// contLog :: a -> a
const contLog = x =>
(console.log(x), cont(x))
// demo
cont (0)
(contLog) // => 0
(when (lt (3)) (contAdd (1)))
(contLog) // => 1
(when (lt (3)) (contAdd (1)))
(contLog) // => 2
(when (lt (3)) (contAdd (1)))
(contLog) // => 3
(when (lt (3)) (contAdd (1)))
(contLog) // => 3
(when (lt (3)) (contAdd (1)))
(contLog) // => 3
这也可以通过承诺很容易地完成:
if (A)
p = A();
else
p = Promise.resolve(defaultValue)
p.then(B).then(done)
假设 A 和 B 都 return 承诺。如果他们是传统的回调接受者,你必须承诺他们。
一个想法是使用这样的承诺设置一个队列来链接异步计算:
function compute(queue, previousResult) {
if (!queue.length) return previousResult;
return new Promise((resolve, reject) => {
//Get Operation
var operation = queue.shift();
//If there is nothing to calculate just resolve the promise
if (!operation[0]) {
resolve(0);
}
//Execute the computation function and give it the
//resolve callback to resolve the promise when the calculations done
operation[1](operation[0], previousResult || null, resolve);
}).then(result => {
//Call the next calculation with result of the previous one
return compute(queue, result);
});
}
然后:
var A = 10;
function computeA(a, previous, resolve) {
$.get('https://httpbin.org/get').then(function() {
resolve(a*10);
});
}
var B = 20;
function computeB(b, previous, resolve) {
resolve(previous + b * 10);
}
var C = 20;
function computeC(c, previous, resolve) {
resolve(previous + c * 10);
}
compute([
[A, computeA],
[B, computeB],
[C, computeC]
]).then(function(result) {
console.log(result);
});
演示:
function compute(queue, previousResult) {
if (!queue.length) return previousResult;
return new Promise((resolve, reject) => {
//Get Operation
var operation = queue.shift();
//If there is nothing to calculate just resolve the promise
if (!operation[0]) {
resolve(0);
}
//Execute the computation function
operation[1](operation[0], previousResult || null, resolve);
}).then(result => {
//Call the next calculation with result of the previous one
return compute(queue, result);
});
}
var A = 10;
function computeA(a, previous, resolve) {
$.get('https://httpbin.org/get').then(function() {
resolve(a*10);
});
}
var B = 20;
function computeB(b, previous, resolve) {
resolve(previous + b * 10);
}
var C = 20;
function computeC(c, previous, resolve) {
resolve(previous + c * 10);
}
compute([
[A, computeA],
[B, computeB],
[C, computeC]
]).then(function(result) {
console.log(result);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
假设我必须做一些计算,例如包括计算 A(异步,带回调),然后在计算 A 时,计算 B(也是异步带回调)。
问题是A是可选的,如果没有数据,我应该直接跳到计算B。
目前我使用非常难看的代码和双倍的 B 执行,像这样:
if (A)
computeA(A,() => computeB(B,() => console.log("done.")));
else
computeB(B,() => console.log("done."));
我要求避免重复我的一些解决方案,任何 JS 开发人员都无法识别这些解决方案:-)是否有一些惯用语?
这本质上是延续传球风格。因为延续只是一个函数,所以您可以将总是发生的链分配给一个 var,然后在 if
let next = () => computeB(B,() => console.log("done."))
if (A)
computeA(A, next)
else
next()
一个微小的延续 monad
在 JavaScript 中使用延续真的很有趣,它们以非常有意义的方式表达特定问题。
这是连续 monad cont
的一个小实现,它允许您定义您希望在计算中链接的各种函数。
when
是这里的关键函数,因为它允许您指定一个有条件地绑定链中另一个函数的谓词。
下面,我们将从 0
的延续开始,(cont(0)
) 然后仅当当前延续的值小于 3
时才添加 1
– 后续对 when (lt (3)) (f)
的调用将 return 未更改值的延续
// cont :: a -> cont a
const cont = x =>
k => k (x)
// when :: (a -> Bool) -> (a -> cont a) -> a -> cont a
const when = p => f => x =>
p (x) ? f (x) : cont (x)
// contAdd :: Number -> Number -> cont Number
const contAdd = x => y =>
cont (x + y)
// lt :: Number -> Number -> Bool
const lt = x => y =>
y < x
// contLog :: a -> a
const contLog = x =>
(console.log(x), cont(x))
// demo
cont (0)
(contLog) // => 0
(when (lt (3)) (contAdd (1)))
(contLog) // => 1
(when (lt (3)) (contAdd (1)))
(contLog) // => 2
(when (lt (3)) (contAdd (1)))
(contLog) // => 3
(when (lt (3)) (contAdd (1)))
(contLog) // => 3
(when (lt (3)) (contAdd (1)))
(contLog) // => 3
这也可以通过承诺很容易地完成:
if (A)
p = A();
else
p = Promise.resolve(defaultValue)
p.then(B).then(done)
假设 A 和 B 都 return 承诺。如果他们是传统的回调接受者,你必须承诺他们。
一个想法是使用这样的承诺设置一个队列来链接异步计算:
function compute(queue, previousResult) {
if (!queue.length) return previousResult;
return new Promise((resolve, reject) => {
//Get Operation
var operation = queue.shift();
//If there is nothing to calculate just resolve the promise
if (!operation[0]) {
resolve(0);
}
//Execute the computation function and give it the
//resolve callback to resolve the promise when the calculations done
operation[1](operation[0], previousResult || null, resolve);
}).then(result => {
//Call the next calculation with result of the previous one
return compute(queue, result);
});
}
然后:
var A = 10;
function computeA(a, previous, resolve) {
$.get('https://httpbin.org/get').then(function() {
resolve(a*10);
});
}
var B = 20;
function computeB(b, previous, resolve) {
resolve(previous + b * 10);
}
var C = 20;
function computeC(c, previous, resolve) {
resolve(previous + c * 10);
}
compute([
[A, computeA],
[B, computeB],
[C, computeC]
]).then(function(result) {
console.log(result);
});
演示:
function compute(queue, previousResult) {
if (!queue.length) return previousResult;
return new Promise((resolve, reject) => {
//Get Operation
var operation = queue.shift();
//If there is nothing to calculate just resolve the promise
if (!operation[0]) {
resolve(0);
}
//Execute the computation function
operation[1](operation[0], previousResult || null, resolve);
}).then(result => {
//Call the next calculation with result of the previous one
return compute(queue, result);
});
}
var A = 10;
function computeA(a, previous, resolve) {
$.get('https://httpbin.org/get').then(function() {
resolve(a*10);
});
}
var B = 20;
function computeB(b, previous, resolve) {
resolve(previous + b * 10);
}
var C = 20;
function computeC(c, previous, resolve) {
resolve(previous + c * 10);
}
compute([
[A, computeA],
[B, computeB],
[C, computeC]
]).then(function(result) {
console.log(result);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>