我有一个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完全完成之前,
答案 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
,第一个元素被删除。这将移动整个阵列。通常,在迭代数组时,您不希望对其进行突变。这可以通过以下两种方法之一解决:
使用
0
代替,并在完成迭代后拼接所有元素。for (const data of dataBuffer)
(清除数组)。改为使用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);