异步/等待功能的行为异常

时间:2020-10-16 17:09:14

标签: javascript node.js async-await

我有一个nodejs / javascript应用程序,并且与异步/等待有关。我检查了这里给出的许多建议,但找不到适合我问题的建议。如果没有,我事先表示歉意。

我有一个从websocket流中获取信息的功能。此信息已缓冲,等待进一步处理。

使用setTimeout每秒调用一次处理流中信息的函数(updateOrderBook)。

为了能够处理流数据,必须存在一个订单簿。如果没有,则应创建一个。该函数(createOrderBook)需要从REST API获取数据,这就是我遇到的问题。

在updateOrderBook中,我正在等待createOrderBook函数。 createOrderBook依次等待axios返回REST API数据。但是,在等待此API数据的同时,似乎控件已返回给updateOrderBook。

那么我在这里俯瞰什么?更重要的是我该如何解决?

const updateOrderBook = () => {
  // select the coin at hand
  dataBuffer.forEach(async (data, index) => {
    dataBuffer.splice(index, 1);
    let symbol = data.s.toLowerCase();

    if (orderBook.filter(orders => orders.coin === symbol).length === 0) {
      await createOrderBook(symbol);
      console.log(`${symbol}: Orderbook created`);
    }

    if (typeof lastUpdateId[symbol] == "undefined") {
      console.log(
        symbol,
        "UNDEFINED",
        orderBook.filter(orders => orders.coin === symbol).length === 0
      );
    }
    ...
  setTimeout(() => updateOrderBook(), 1000);
};
const createOrderBook = async (symbol) => {
  console.log(symbol, "ENTERED COB");
  orderBook.push({ coin: symbol, bids: [], asks: [] });
  firstEvent[symbol] = true;
  try {
    const response = await axios.get(
      `https://www.binance.com/api/v3/depth?symbol=${symbol.toUpperCase()}&limit=1000`
    );
    console.log(symbol, "BEFORE ENTERING HIO");
    handleInitialOrderBook(response, symbol.toLowerCase());
    console.log(symbol, "COB");
  } catch (error) {
    console.log(symbol, error);
  }
};

期望的日志:

xrpusdt BEFORE ENTERING HIO
xrpusdt HIO
xrpusdt COB
xrpusdt: Orderbook created

实际日志:

xrpusdt ENTERED COB
xrpusdt UNDEFINED false
xrpusdt BEFORE ENTERING HIO
xrpusdt HIO
xrpusdt COB
xrpusdt: Orderbook created

正如您所看到的,我希望发生未定义的日志,如果有的话,在createOrderBook完成之后,而createOrderBook完全完成之前,

1 个答案:

答案 0 :(得分:3)

问题出在:

dataBuffer.forEach(async (data, index) => {
  // ...
});

尽管回调函数是异步函数。 forEach不会检查此回调的返回值,也不会在调用下一个迭代之前等待返回的诺言。

当前缓冲区中的第一个元素正在等待createOrderBook完成,而第二个元素跳过if-block:

if (orderBook.filter(orders => orders.coin === symbol).length === 0)

这是因为createOrderBook在等待之前调用orderBook.push(...),因此该元素已经被推送,并且JavaScript引擎将在需要{时立即移动forEach中的下一个元素await中的{1}}。

那么您如何解决呢?最简单的方法是不使用createOrderBook调用,而使用for...of。因为您没有创建新的回调函数,所以主函数将等待promise,并且仅在解决后继续运行。

forEach

为此,您必须将for (const [index, data] of dataBuffer.entries()) { // ... } 更改为异步函数。上面的代码块假定updateOrderBook是一个数组,如果它是另一个对象,则可能必须检查有关如何同时获取带有索引和元素的迭代器的文档。

注意:通过在循环中使用dataBuffer,您会得到一些奇怪的行为。在第一个元素上dataBuffer.splice(index, 1)index,第一个元素被删除。这将移动整个阵列。通常,在迭代数组时,您不希望对其进行突变。这可以通过以下两种方法之一解决:

  1. 使用0代替,并在完成迭代后拼接所有元素。 for (const data of dataBuffer)(清除数组)。

  2. 改为使用while循环。

    dataBuffer.slice(0)

while (dataBuffer.length > 0) {
  const data = dataBuffer.shift();
  // ...
}


const letters = ["a", "b", "c", "d", "e", "f", "g"];

for (const [index, letter] of letters.entries()) {
  console.log(index, letter);
  letters.splice(index, 1);
}

console.log(letters);