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>