admin管理员组

文章数量:1391995

I'm using websocket to send and receive data (up to 30 small messages per seconds). I want the client to send a websocket payload and wait for a specific message from the server.

Flow:

The client send a request

It also store the requestId (163) in waitingResponse object as a new object with sent timestamp

waitingResponse = {
  163: { sent: 1583253453549 }
}

When the server response, another function validate the payload and then append the result to that request object

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

The client checks every x ms that object for the action key

I have a function sendPayload that sends the payload and then await for a value from awaitResponse (the function below). Right now this function doesn't work. I tried making 2 separate function, one that would be the setTimeout timer, the other was the promise. I also tried having both in the same function and decide if it was the loop or the promise with the original argument you can see below. Now I'm thinking that function should always return a promise even in the loop but I cannot seem to make that work with a timer and I'm afraid of the cost of multiple promise in one another. Let's say I check for a response every 5 ms and the timeout it 2000ms. That is a lot of promises.

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] };

    }
}

Note: I put the websocket tag below because I looked hard for that. Maybe I came across the solution without even realising it, but if you have better tags for this question to be found easier, please edit them :)

I'm using websocket to send and receive data (up to 30 small messages per seconds). I want the client to send a websocket payload and wait for a specific message from the server.

Flow:

The client send a request

It also store the requestId (163) in waitingResponse object as a new object with sent timestamp

waitingResponse = {
  163: { sent: 1583253453549 }
}

When the server response, another function validate the payload and then append the result to that request object

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

The client checks every x ms that object for the action key

I have a function sendPayload that sends the payload and then await for a value from awaitResponse (the function below). Right now this function doesn't work. I tried making 2 separate function, one that would be the setTimeout timer, the other was the promise. I also tried having both in the same function and decide if it was the loop or the promise with the original argument you can see below. Now I'm thinking that function should always return a promise even in the loop but I cannot seem to make that work with a timer and I'm afraid of the cost of multiple promise in one another. Let's say I check for a response every 5 ms and the timeout it 2000ms. That is a lot of promises.

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] };

    }
}

Note: I put the websocket tag below because I looked hard for that. Maybe I came across the solution without even realising it, but if you have better tags for this question to be found easier, please edit them :)

Share Improve this question edited Mar 3, 2020 at 23:37 HypeWolf asked Mar 3, 2020 at 16:58 HypeWolfHypeWolf 8502 gold badges15 silver badges33 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 6

Yeah, you're mixing a lot of callback style functions with intermediate promises and async/await. Don't do polling when waiting for a response! Instead, when writing a queue, put the resolve function itself in the queue so that you can directly fulfill/reject the respective promise from the response handler.

In your case:

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);
    }
}

You might even do away with the request object altogether and only store the resolve function itself, if the processMessage function does not need any details about the request.

本文标签: javascriptWebsocket waiting for server response with a queueStack Overflow