【问题标题】:Repeat async function until true重复异步函数直到为真
【发布时间】:2021-06-24 11:56:15
【问题描述】:

我有一个 async 函数来检查订单的状态 (checkOrderStatus())。我想重复这个函数,直到它返回"FILLED""CANCELED",然后在另一个函数中使用这个返回值来决定是继续还是停止代码。每个订单在成为"FILLED""CANCELED" 之前都会经历不同的状态,因此需要重复checkOrderStatus() 函数(这是一个API 调用)。

我现在拥有的是这个,重复 checkOrderStatus() 函数:

const watch = filter => {
    return new Promise(callback => {
        const interval = setInterval(async () => {
            if (!(await filter())) return;
            clearInterval(interval);
            callback();
        }, 1000);
    });
};

const watchFill = (asset, orderId) => {
    return watch(async () => {
        const { status } = await checkOrderStatus(asset, orderId);

        console.log(`Order status: ${status}`);

        if (status === 'CANCELED') return false;
        return status === 'FILLED';
    });
};

然后我从另一个函数调用watchFill(),我想检查它的返回值(truefalse)如果true 则继续代码,如果false 则停止代码:

const sellOrder = async (asset, orderId) => {
    try {
        const orderIsFilled = await watchFill(asset, orderId);
        
        if (orderIsFilled) {
            //… Continue the code (status === 'FILLED'), calling other async functions …
        }
        else {
            //… Stop the code
            return false;
        }
    }
    catch (err) {
        console.error('Err sellIfFilled() :', err);
    }
};

但是,这不起作用。我可以通过watchFill() 中的console.log 看到终端中正在更新的状态,但它永远不会停止,最重要的是,sellOrder() 中的orderIsFilled 变量中的值不会更新,无论返回的值是什么watchFill() 变为。

我怎样才能达到预期的行为?

【问题讨论】:

标签: javascript node.js async-await promise


【解决方案1】:

如果filter 解析为false,则watch 函数会在第一次调用后清除间隔计时器。 setInterval 也不会等待异步函数完成执行,因此您必须自己创建一个循环。试试这个:

const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));

const watch = async check => {
    while (true) {
        if (await check()) {
            return;
        }

        await delay(1000);
    }
};

因为watch 只在check 成功时才解析,所以不可能失败所以你不需要检查它(这可能是你代码中的一个错误):

const sellOrder = async (asset, orderId) => {
    try {
        await watchFill(asset, orderId);
        
        //… Continue the code (status === 'FILLED'), calling other async functions …
    }
    catch (err) {
        console.error('Err sellIfFilled() :', err);
    }
};

p-wait-for 包含一个很好的实现。你可以这样使用它:

import pWaitFor from 'p-wait-for';

const watchFill = (asset, orderId) => pWaitFor(async () => {
    const { status } = await checkOrderStatus(asset, orderId);

    console.log(`Order status: ${status}`);

    if (status === 'CANCELED') return false;
    return status === 'FILLED';
}, {
    interval: 1000,
    leadingCheck: false
});

【讨论】:

  • 感谢您的快速答复。我会试一试。但是,您知道如何在 vanilla JS 中实现这一点吗?谢谢
  • 更新了我的答案
  • check() 回调应该是我的checkOrderStatus 函数吗?如果是这样,用法是这样的吗:while (true) { if (await check() === 'FILLED') return true; else if (await check() === 'CANCELED') return false; }
  • watch函数的用法没有改变,只是变量的名字。这意味着您当前调用 watch 函数的代码现在可以工作了。
  • 所以我现在会使用watchFill,但只需将watch 更改为您展示的示例?
【解决方案2】:

您可以像这样使用递归功能:

const checkOrderStatus = async () => {
    // ... function does some work ...
    await someOtherFunction() // you can use here the other async function as well
    // ... function does some more work after returning from await ...
    if(/* if status is FILLED or CANCELED */) {
        // return true or false or some info about response for your needs
    } else {
        checkOrderStatus();
    }
}

// this will response back when status will be FILLED or CANCELED
await checkOrderStatus();

【讨论】:

  • 我不应该在else 中的checkOrderStatus() 之前使用await 吗?
  • 不,你不需要那个。注意:由于在 await 之后调用堆栈是空的,这意味着调用堆栈不会随着每次递归调用而增长。因此,您的调用堆栈永远不会达到大小限制,并且递归会一直持续下去。
  • 谢谢。我尝试使用您编写的函数,使用 if (status === 'FILLED') return true; else if (status === 'CANCELED') return false; else { checkOrderStatus() },但是当我尝试在我的其他函数中执行 const orderIsFilled = await checkOrderStatus(asset, orderId) 时,orderIsFilled 包含未定义,因此返回 false(sellOrder() 中的 else),但是我可以在checkOrderStatus 中的console.log 中看到状态已填充。知道为什么吗?
  • 毫不拖延地,这不会保留 OP 的意图。似乎还有很多东西可以想象。
【解决方案3】:

watch 从不使用任何值调用resolve(在原始代码中,这被误导性地命名为callback()),因此const orderIsFilled = await watchFill(asset, orderId); 将无法使用除undefined 之外的任何值填充orderIsFilled

如果将await filter() 的结果保存在变量中并将其传递给 callbackcallback(result),你的代码看起来应该可以工作。

也就是说,可以通过使用循环和编写简单的wait 函数来简化代码。这样,您可以返回一个值(比弄清楚如何/何时调用resolve 更自然),使new Promise 模式远离逻辑,避免处理setInterval 和随之而来的簿记。

const wait = ms =>
  new Promise(resolve => setTimeout(resolve, ms))
;

const watch = async (predicate, ms) => {
  for (;; await wait(ms)) {
    const result = await predicate();
    
    if (result) {
      return result;
    }
  }
};

/* mock the API for demonstration purposes */
const checkOrderStatus = (() => {
  let calls = 0;
  return async () => ({
    status: ++calls === 3 ? "FILLED" : false
  });
})();

const watchFill = (asset, orderId) =>
  watch(async () => {
    const {status} = await checkOrderStatus();
    console.log(`Order status: ${status}`);
    return status === "CANCELLED" ? false : status === "FILLED";
  }, 1000)
;

const sellOrder = async () => {
  try {
    const orderIsFilled = await watchFill();
    console.log("orderIsFilled:", orderIsFilled);
  }
  catch (err) {
    console.error('Err sellIfFilled() :', err);
  }
};
sellOrder();

【讨论】:

  • 我不明白为什么我需要检查calls 并在checkOrderStatus 中对其进行迭代,因为它是一个API 调用?我不能只调用 API 并执行 if (status === 'FILLED') return true; else if (status === 'CANCELED') return false; 吗?
  • 不,我只是在模拟该函数,以便您可以运行该示例。你提到的if (status... 代码还在,我只是把它做成三元组,因为我更喜欢三元组。正如我在帖子中提到的,如果您只是将await filled() 保存在一个变量中并将其传递给callback,那么您应该很高兴。 sn-p 只是一个清理工作和功能证明。
  • 我在checkOrderStatus 中使用我的API 调用尝试了您的解决方案,但我不工作。值得注意的是我的checkOrderStatusasync 并返回一个Promise 吗?
  • 具体有什么不好的地方?如您所见,我的checkOrderStatus 是异步的。如果您正在努力使现有答案起作用(this answer 与我的基本相同),那么您向我们展示的代码可能不足以说明您的原始程序中真正发生的情况。 .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-07
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
  • 2015-12-01
  • 2019-12-21
相关资源
最近更新 更多