Home > Back-end >  Coffeescript wait until flag is set from a callback and then move on to the next part of the code
Coffeescript wait until flag is set from a callback and then move on to the next part of the code

Time:09-23

I have here a simple program using coffeescript which creates a server on a Raspberry pi, and this receives data from a python script running on a Windows machine on the same network, does some calculations with that data and sends back the results.

What I would like to do, is keep the server open, running and accepting data until a parameter sent by the python script asks it to close.

To try this, I have created a loop which blocks the code until these flags are set; however, this is, well, blocking the code so the flags are not being set (the python script is not even able to connect). So I imagine I need to use some sort of asynchronous control here/promises.

# Functions
process_data = (data) ->
    double_value: data * 2
    triple_value: data * 3

clean_up_actions = () ->
    console.log "Cleaning up..."

# Create server
net = require('net')
domain = '10.1.1.28'
port = 9001

program_complete = false
clean = false

console.log("Creating server...")
server = net.createServer (socket) ->
    console.log "#{socket.remoteAddress} has connected"

    # When data is received, do something and send back a response
    socket.on 'data', (data) ->
        console.log("Data received...")
        if not program_complete
            incoming_data = JSON.parse(data)
            
            some_useful_value = incoming_data["some_useful_value"]
            result = process_data(some_useful_value)
            socket.write JSON.stringify(result)
            
            # Check if end of program
            program_complete = incoming_data["end_program"]
            if program_complete
                clean_up_actions()
                clean = true

console.log "Listening to #{domain}:#{port}"
server.listen port, domain
    
console.log("Waiting until complete...")
loop
    break if program_complete and clean
console.log "--- PROGRAM FINISHED ---"

To test this, I have used Q.delay(10000) instead of the loop, and it works how I expect it to work (I get a connection and can pass data back and forth not problem), except instead of exiting when the end_program is set to True, it will exit after 10 seconds (as expected).

...
# console.log("Waiting until complete...")
# loop
#     break if program_complete and clean
# console.log "--- PROGRAM FINISHED ---"

Q = require('q')
Q.delay(10000).then ->
    console.log "Done"
.finally ->
  console.log('--- PROGRAM END ---')
  process.exit(0)

My question is, how can I use a promise or other asynchronous control (like Q.delay(n)) but instead of delaying for a time, it waits until these flags are set.

If interested, or want to test it, the python code is below

import socket
import json
import time

REMOTE_HOST = '10.1.1.28'
REMOTE_PORT = 9001

# Create a socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect to the remote host and port
sock.connect((REMOTE_HOST, REMOTE_PORT))

# Send some data
data_dictionary = {
    'some_useful_value' : 21,
    'end_program' : False
}
data_to_send = json.dumps(data_dictionary).encode('utf-8')
sock.send(data_to_send)
print(sock.recv(1024))

time.sleep(0.5)

# Send some data, but this time, end
data_dictionary = {
    'some_useful_value' : 13,
    'end_program' : True
}
data_to_send = json.dumps(data_dictionary).encode('utf-8')
sock.send(data_to_send)
print(sock.recv(1024))

time.sleep(0.5)

# Terminate
sock.close()

CodePudding user response:

There are probably a few ways you can achieve this, where we wait until your flag end_program or clean from your code is set to true by your socket.on 'data' method.

At the end of the script, you can use a function that continuously returns a promise until a certain condition is met (in this case, the flag clean is set).

The Q library you are using should suffice as I believe its .delay function is async. For example, in the below code, the function checks the flag every second, and if the flag is not set, the function will call itself again, checking after another second. If the flag is set, we can then move on to the next part of the code.

This may look something like this, using the variable clean as your flag:

waitForFlag = () -> 
  Q.delay(1000).then -> 
    if (clean)
      console.log "Condition met! We can move on"
    else
      # if condition isn't met, check again in one second
      waitForFlag() 

# Call function
waitForFlag().then ->
  process.exit(0)

CodePudding user response:

My question is, how can I use a promise or other asynchronous control (like Q.delay(n)) but instead of delaying for a time, it waits until these flags are set.

I think the short answer is no, or at least not without digging into some more esoteric stuff like interrupts. But you can approximate that "manually" by setting up a short wait, checking if the value is set and then either waiting again or exiting the wait-status and moving on with the rest of your logic based on the result of that check.

I'm not familiar with the Q library you're importing here (and invoking with Q.delay) but I think you're on the right path. In the general case I think what you want to do is use setInterval or setTimeout to essentially "yield" the execution for some relatively short time then check if the value has been set. I imagine that Q.delay is using setTimeout or similar internally.

Here's a rough sketch of what I mean:

# here is the flag we'll wait to be set
the_flag = null 

# here is an arbitrary callback method that represents
# the "action" to take once the flag is set
the_callback = (flag)->
  console.log "#{(new Date()).toISOString()}: the_flag is now set to:", flag

# we'll keep a handle to the value returned by setInterval
# so we can cancel it later.
interval_id = null

# here's the method that checks if the flag has been set
# and responds accordingly
check_if_flag_is_set = ()->
  if the_flag?
    # it *is* set now, so clear the interval and callback
    clearInterval(interval_id)
    interval_id = null
    the_callback(the_flag)
  else
    console.log "#{(new Date()).toISOString()}: the flag is not yet set, still waiting..."

# now just kick off the interval to invoke the "check"
# method every 3 seconds
interval_id = setInterval( check_if_flag_is_set, 3000 )

Now if/when the value of the_flag is set in some asynchronous code the check_if_flag_is_set method will detect it and report the result to the_callback. Note that the setInterval wait period is non-blocking - other code can and will execute while we're waiting on the next invocation of check_if_flag_is_set.

That said, the network i/o interaction is also non-blocking, so if the flag is being set by some data being written to the socket you don't really need an extra layer of asynchronous, non-blocking waiting via setInterval. You could just wait for the on('data',handler) or similar event to be emitted by your network listener.

  • Related