Lua ignores scope variable
This may be a silly question, however, I don't know what's going on.
I have a simple script that picks google time and I need to set it to a global variable time
. This way, inside the event, receive
I print the selected time and it works correctly.
The problem is that the variable is time
always empty when called outside the event. Here is the code:
-- test.lua
time = ""
function getTime()
conn = net.createConnection(net.TCP, 0)
conn:connect(80,'google.com')
conn:on("connection", function(conn, payload)
conn:send("HEAD / HTTP/1.1\r\n"..
"Host: google.com\r\n"..
"Accept: */*\r\n"..
"User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)"..
"\r\n\r\n"
)
end)
conn:on("receive", function(conn, payload)
conn:close()
time = string.sub(payload,string.find(payload,"Date: ")
+6,string.find(payload,"Date: ")+35)
end)
print("testing: " .. time) -- WORKS!
end
getTime()
print("variable: ".. time)
This is how I call the function (using terminal nodemcu-uploader):
β test nu terminal
--- Miniterm on /dev/cu.wchusbserial1410 115200,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
> dofile('lib/test.lua')
variable:
> testing: Sat, 20 May 2017 01:37:35 GMT
Any help would be really appreciated! Thanks to
source to share
The NodeMCU programming model is similar to Node.js, only in Lua. It is asynchronous and event driven. Therefore, many functions have parameters for callback functions.
Source: https://github.com/nodemcu/nodemcu-firmware/#programming-model
This means that functions that take callback functions as parameters are not blocked. What it returns means that you can't just read a piece of code in turn and expect it to be executed in that order.
So, the sequence of events in the original program is something like this:
-
getTime
runs but does not block. Performed -
print("variable: ".. time)
...time
is still empty at this point. - A connection to google.com has been established.
- The HEAD request is sent to google.com.
- The response is a receive handler for the receive and receive event (i.e. an anonymous callback function).
-
time
is filled.
I see two obvious fixes, one using your global variable time
and one without it. Both are based on the pass-call-function-as-parameter pattern.
Note that you should always set up event listeners ( conn:on
in your case) prior to firing these events ( conn:connect
) to avoid some events. Your code
conn:connect(80,'google.com')
conn:on("connection"...
works only because it is conn:connect
not blocked and because it takes some time until the connection is established. By the time this happens, an on-connection event handler has been registered.
Saving a global variable
time = ""
function getTime(cb)
conn = net.createConnection(net.TCP, 0)
conn:on("connection", function(socket, payload)
socket:send("HEAD / HTTP/1.1\r\n" ..
"Host: google.com\r\n" ..
"Accept: */*\r\n" ..
"User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)" ..
"\r\n\r\n")
end)
conn:on("receive", function(socket, payload)
socket:close()
time = string.sub(payload, string.find(payload, "Date: ")
+ 6, string.find(payload, "Date: ") + 35)
print("time inside on-receive: " .. time)
cb()
end)
conn:connect(80, 'google.com')
end
function do_something_with_time()
print("time inside callback: " .. time)
end
getTime(do_something_with_time)
Without a global variable
function getTime(cb)
conn = net.createConnection(net.TCP, 0)
conn:on("connection", function(socket, payload)
socket:send("HEAD / HTTP/1.1\r\n" ..
"Host: google.com\r\n" ..
"Accept: */*\r\n" ..
"User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)" ..
"\r\n\r\n")
end)
conn:on("receive", function(socket, payload)
socket:close()
local time = string.sub(payload, string.find(payload, "Date: ")
+ 6, string.find(payload, "Date: ") + 35)
print("time inside on-receive: " .. time)
cb(time)
end)
conn:connect(80, 'google.com')
end
function do_something_with_time(time)
print("time inside callback: " .. time)
end
getTime(do_something_with_time)
source to share