Websocket ожидает ответа сервера с очередью - PullRequest
0 голосов
/ 03 марта 2020

Я использую websocket для отправки и получения данных (до 30 маленьких сообщений в секунду). Я хочу, чтобы клиент отправил полезную нагрузку веб-сокета и дождался определенного сообщения c с сервера.

Поток:

Клиент отправил запрос

Он также хранит requestId (163) в объекте waitingResponse как новый объект с sent отметкой времени

waitingResponse = {
  163: { sent: 1583253453549 }
}

Когда сервер отвечает, другая функция проверяет полезную нагрузку и затем добавляет результат к этому объекту запроса

waitingResponse = {
  163: { sent: 1583253453549, action: "none" }
}

Клиент каждые x мс проверяет этот объект на предмет ключа action

У меня есть функция sendPayload, которая отправляет полезную нагрузку и затем ожидает значения от awaitResponse (функция ниже). Сейчас эта функция не работает. Я попытался сделать две отдельные функции, одна из которых была бы таймером setTimeout, другая была обещанием. Я также попытался использовать обе функции в одной и той же функции и решить, был ли это l oop или обещание с аргументом original, которое вы можете увидеть ниже. Теперь я думаю, что функция всегда должна возвращать обещание даже в l oop, но я не могу заставить это работать с таймером, и я боюсь стоимости многократного обещания друг в друге. Допустим, я проверяю ответ каждые 5 мс и время ожидания составляет 2000 мс. Это много обещаний.

public async sendPayload(details) {
    console.log("sendPlayload", details);

    this.waitingResponse[details.requestId] = { sent: +new Date() };

    if (this.socket.readyState === WebSocket.OPEN) {
        this.socket.send(JSON.stringify(details));
    }
    const bindAwaitResponse = this.awaitResponse.bind(this);
    return new Promise(async function (resolve, reject) {
        const result = await bindAwaitResponse(details.requestId, true);
        console.log("RES", result);
        console.info("Time took", (+new Date() - result.sent) / 1000);

        resolve(result);
    });

}

public async awaitResponse(requestId, original) {
    // console.log(requestId, "awaitResponse")
    return new Promise((resolve, reject) => {
        // Is it a valid queued request
        if (this.waitingResponse[requestId]) {
            // Do we have an answer?
            if (this.waitingResponse[requestId].hasOwnProperty("action")) {
                console.log(requestId, "Got a response");
                const tmp = this.waitingResponse[requestId];
                delete this.waitingResponse[requestId]; // Cleanup
                resolve(tmp);
            } else {
                // No answer yet from remote server
                // console.log("no answer: ", JSON.stringify(this.waitingResponse));
                // Check if request took too long
                if (+new Date() - this.waitingResponse[requestId].sent > 5000) { // TODO: Option for time out
                    console.warn(requestId, "Request timed out");

                    // Timed out, result took too long
                    // TODO: Option, default action when timed out
                    delete this.waitingResponse[requestId];  // Cleanup
                    resolve({
                        action: "to" // For now, just sent a timeout action, maybe the default action should be outside of the Network class?
                    })
                } else {
                    // console.log(requestId, "Still waiting for results");
                    console.log(JSON.stringify(this.waitingResponse));
                    // Still waiting, after x ms, recall function
                    return setTimeout(async () => { resolve(await this.awaitResponse(requestId, false)); }, 200);
                }
            }
        }
    });
}

private async processMessage(msg) {
    console.log("WS received Message", JSON.stringify(msg.data));

    console.log("Current: ", JSON.stringify(this.waitingResponse));

    let data = JSON.parse(msg.data);
    // console.log("Received: ", data);


    if (data.hasOwnProperty("requestId") && this.waitingResponse[data.requestId]) {
        // console.log("processMessage ID found");
        this.waitingResponse[data.requestId] = { ...data, ...this.waitingResponse[data.requestId] };

    }
}

Примечание: я поставил тег websocket ниже, потому что я искал это усердно. Возможно, я натолкнулся на решение, даже не осознавая его, но если у вас есть лучшие теги для этого вопроса, которые можно найти проще, отредактируйте их:)

1 Ответ

0 голосов
/ 04 марта 2020

Да, вы смешиваете множество функций стиля обратного вызова с промежуточными обещаниями и async / await. Не проводите опрос, ожидая ответа! Вместо этого, когда записывает очередь , поместите саму функцию resolve в очередь, чтобы вы могли напрямую выполнить / отклонить соответствующее обещание из обработчика ответа.

В вашем случае:

public async sendPayload(details) {
    const request = this.waitingResponse[details.requestId] = { sent: +new Date() };
    try {
        if (this.socket.readyState === WebSocket.OPEN) {
           this.socket.send(JSON.stringify(details));
        }
        const result = await new Promise(function(resolve) {
            request.resolve = resolve;

            setTimeout(() => {
                reject(new Error('Timeout')); // or resolve({action: "to"}), or whatever
            }, 5000);
        });
        console.info("Time took", (+new Date() - request.sent) / 1000);
        return result; // or {...request, ...result} if you care
    } finally {
        delete this.waitingResponse[details.requestId];
    }
}


private async processMessage(msg) {
    let data = JSON.parse(msg.data);

    if (data.hasOwnProperty("requestId") {
        const request = this.waitingResponse[data.requestId]
        if (request)
            request.resolve(data)
        else
            console.warn("Got data but found no associated request, already timed out?", data)
    } else {
        console.warn("Got data without request id", data);
    }
}

Вы можете даже полностью отказаться от объекта request и сохранить только саму функцию resolve, если функция processMessage не требует каких-либо подробностей о запросе.

...