Variable scope in javascript module

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Variable scope in javascript module

wld75
Hi team !,
Regarding the sample here :
https://www.nginx.com/blog/batching-api-requests-nginx-plus-javascript-module/

I have an issue trying to use JS module : variable hoisting and global/local
scope doesn't behave as expected. When i try to reassign the resp variable
(as you do after declaring it), the value assigned in the done function is
not brought outside of the done function to the bacthAPI function.
So if resp is initialised with 0 and reassign as 1 in the done funtion, at
the end, resp would = 0.
I had a look to explanation here
https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/
and seems to behave differently in nginx implementation of JS.

Would it be some OS settings outside of NGINX preventing this to work as it
should normally work with javascript ? Any dependency on an OS package ?

Thanks !
BR
Alex

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281699#msg-281699

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

Dmitry Volyntsev

Hi Alex,

Can you, please, share your code?

You can also try to play with njs code in the command line interface.

For example:

njs
interactive njs 0.2.3

v.<Tab> -> the properties and prototype methods of v.
type console.help() for more information

>>function my(){var g = 'init'; console.log(g); (function() {g = 'anon'})(); console.log(g) }; my()
'init'
'anon'
undefined

CLI, is also available in docker
docker run -i -t nginx:latest /usr/bin/njs



> On 26 Oct 2018, at 09:16, alweiss <[hidden email]> wrote:
>
> Hi team !,
> Regarding the sample here :
> https://www.nginx.com/blog/batching-api-requests-nginx-plus-javascript-module/
>
> I have an issue trying to use JS module : variable hoisting and global/local
> scope doesn't behave as expected. When i try to reassign the resp variable
> (as you do after declaring it), the value assigned in the done function is
> not brought outside of the done function to the bacthAPI function.
> So if resp is initialised with 0 and reassign as 1 in the done funtion, at
> the end, resp would = 0.
> I had a look to explanation here
> https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/
> and seems to behave differently in nginx implementation of JS.
>
> Would it be some OS settings outside of NGINX preventing this to work as it
> should normally work with javascript ? Any dependency on an OS package ?
>
> Thanks !
> BR
> Alex
>
> Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281699#msg-281699
>
> _______________________________________________
> nginx mailing list
> [hidden email]
> http://mailman.nginx.org/mailman/listinfo/nginx

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

wld75
Hi Dmitry, thanks for your reply.

Here is my code called using js_content authorize; from a location {}
I want to say "if at least one subrequest answers HTTP/200, set the
final_status to 200 and at then end, return 200"

First thing is that i must declare requestCount with var. If i don't, i get
a "requestCount" is not defined
I thought in javascript, you could declare a variable just using its name
with the "var" keyword to make it global.
Same for final_status if not declared with var. But using var, i'm not able
to update the final status from the "done" function.

I think i missing something in the hoisting behaviour of javascript …
Thanks !

request for REST answers 200 as well as OASS so we should "break" after the
first subrequest but final_status next to the subrequest is always evaluated
to 403 even if previous subrequest returned 200.
The only case were it works, it is if the last subrequest return 200 which
doesn't rely on final_status value.

CODE :

function authorize(req, res) {
    req.warn('Variables init ...');
    var n = 0;
    var final_status = 403;
    var servicesCodes = ['rest','oass'];
    var requestCount = servicesCodes.length;
   
    function done(reply) { //Callback for completed subrequests ...
        n++;
            reply.warn('at start of done function, n is :' + n);
        if (n < requestCount) { //| final_status !=200) {
                reply.warn('status of subrequest is :' + reply.status);
            if (reply.status == 200) {
                    reply.warn('lets set final_status to 200');
                final_status = 200;
                reply.warn('Value of final_status :' + final_status);
                reply.warn('!!! We return 200 because we have this one at
least, no matter if other are 404 !!!');
                res.return(200);
            }
        } else { // last response
            reply.warn('status of last subrequest is :' + reply.status);
            if (reply.status == 200) {
               reply.warn('lets set final_status for last subrequest to
200');
                final_status = 200;
                reply.warn('Value of final_status after last subrequest:' +
final_status);
                reply.warn('!!! We return the final 200 !!!');
                res.return(200);
            } else { // we did not get any 200
                reply.warn('!!! We return the final 403 !!!');
                res.return(403);
            }
        }
    };

    req.warn('n is :' + n);
    req.warn('final_status is : ' + final_status);
    req.warn('servicesCodes is : ' + servicesCodes);
    req.warn('requestCount is : ' + requestCount);

    for (var i = 0; i < requestCount; i++) {
        req.warn('Final status before sending subrequest to next service is
'+ final_status)
        if (final_status == 200) {
            req.warn('We stop here because we have the 200 !!!');
            break;
        } else {
            req.warn('subrequest is on : ' + servicesCodes[i]);
            req.subrequest("/" + servicesCodes[i] + "/TestDevice1.html", '',
done);
        }
    }
}


OUTPUT :


2018/10/30 16:00:10 [warn] 9220#9220: *15 js: Variables init ...
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: n is :0
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: final_status is : 403
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: servicesCodes is : rest,oass
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: requestCount is : 2
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: Final status before sending
subrequest to next service is 403
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: subrequest is on : rest
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: Final status before sending
subrequest to next service is 403
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: subrequest is on : oass
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: at start of done function, n
is :1
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: status of subrequest is :200
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: lets set final_status to 200
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: Value of final_status :200
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: !!! We return 200 because we
have this one at least, no matter if other are 404 !!!
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: at start of done function, n
is :2
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: status of last subrequest is
:404
2018/10/30 16:00:10 [warn] 9220#9220: *15 js: !!! We return the final 403
!!!

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281746#msg-281746

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

wld75
Here is a sample that works with Java but not with njs :

function my() {
    resp = "Start";
    console.log ('Initial resp is ' + resp);

    function done() {
        resp += "AndContinue";
        console.log('In loop resp is ' + resp)
    }

    done();
}
resp = 'empty'
my();
console.log('End Resp is : ' + resp)

With java :

root@linux3:/etc/nginx# js test.js
Initial resp is Start
In loop resp is StartAndContinue
End Resp is : StartAndContinue


With NJS :

root@linux3:/etc/nginx# njs test.js
ReferenceError: "resp" is not defined in 16


Don't know why it doesn't work the same in both runtime.

Thanks

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281747#msg-281747

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

Dmitry Volyntsev


On 30.10.2018 19:58, alweiss wrote:

> Here is a sample that works with Java but not with njs :
>
> function my() {
>      resp = "Start";
>      console.log ('Initial resp is ' + resp);
>
>      function done() {
>          resp += "AndContinue";
>          console.log('In loop resp is ' + resp)
>      }
>
>      done();
> }
> resp = 'empty'
> my();
> console.log('End Resp is : ' + resp)
>
> With java :
>
> root@linux3:/etc/nginx# js test.js
> Initial resp is Start
> In loop resp is StartAndContinue
> End Resp is : StartAndContinue
>
>
> With NJS :
>
> root@linux3:/etc/nginx# njs test.js
> ReferenceError: "resp" is not defined in 16
>
>
> Don't know why it doesn't work the same in both runtime.

Currently, njs requires all the variables to be declared.
Adding 'var resp;' at the beginning of the file helps.

docker run -i -t nginx:latest /usr/bin/njs
interactive njs 0.2.4

v.<Tab> -> the properties and prototype methods of v.
type console.help() for more information

 >> var resp; function my() { resp = "Start"; console.log ('Initial resp
is ' + resp); function done() { resp += "AndContinue"; console.log('In
loop resp is ' + resp)}; done()}; resp = 'empty'; my(); console.log('End
Resp is : ' + resp)
'Initial resp is Start'
'In loop resp is StartAndContinue'
'End Resp is : StartAndContinue'
undefined
 >>





>
> Thanks
>
> Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281747#msg-281747
>
> _______________________________________________
> nginx mailing list
> [hidden email]
> http://mailman.nginx.org/mailman/listinfo/nginx
>
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

Valentin V. Bartenev-3
In reply to this post by wld75
On Tuesday 30 October 2018 12:58:53 alweiss wrote:

> Here is a sample that works with Java but not with njs :
>
> function my() {
>     resp = "Start";
>     console.log ('Initial resp is ' + resp);
>
>     function done() {
>         resp += "AndContinue";
>         console.log('In loop resp is ' + resp)
>     }
>
>     done();
> }
> resp = 'empty'
> my();
> console.log('End Resp is : ' + resp)
>
> With java :
>
> root@linux3:/etc/nginx# js test.js
> Initial resp is Start
> In loop resp is StartAndContinue
> End Resp is : StartAndContinue
>
>
> With NJS :
>
> root@linux3:/etc/nginx# njs test.js
> ReferenceError: "resp" is not defined in 16
>
>
> Don't know why it doesn't work the same in both runtime.
>
[..]

njs implements "strict mode" of JS:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

It's said in the beginning of documentation: http://nginx.org/en/docs/njs/

  wbr, Valentin V. Bartenev

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

wld75
Thanks guys, this is a first step.
I moved declaration to the top of my script :

********START SCRIPT *************

var n = 0;
var final_status = 403;
var servicesCodes = ['rest','oass'];
var requestCount = servicesCodes.length;

function authorize(req, res) {
    req.warn('Variables init ...');
   
    function done(reply) { //Callback for completed subrequests ...
        n++;
            reply.warn('at start of done function, n is :' + n);
        if (n < requestCount) { //| final_status !=200) {
                reply.warn('status of subrequest is :' + reply.status);
            if (reply.status == 200) {
                    reply.warn('lets set final_status to 200');
                final_status = 200;
                reply.warn('Value of final_status :' + final_status);
                reply.warn('!!! We return 200 because we have this one at
least, no matter if other are 404 !!!');
                res.return(200);
            }
        } else { // last response
            reply.warn('status of last subrequest is :' + reply.status);
            if (reply.status == 200) {
               reply.warn('lets set final_status for last subrequest to
200');
                final_status = 200;
                reply.warn('Value of final_status after last subrequest:' +
final_status);
                reply.warn('!!! We return the final 200 !!!');
                res.return(200);
            } else { // we did not get any 200
                reply.warn('!!! We return the final 403 !!!');
                res.return(403);
            }
        }
    };

    req.warn('n is :' + n);
    req.warn('final_status is : ' + final_status);
    req.warn('servicesCodes is : ' + servicesCodes);
    req.warn('requestCount is : ' + requestCount);

    for (var i = 0; i < requestCount; i++) {
        req.warn('Final status before sending subrequest to next service is
'+ final_status)
        if (final_status == 200) {
            req.warn('We stop here because we have the 200 !!!');
            break;
        } else {
            req.warn('subrequest is on : ' + servicesCodes[i]);
            req.subrequest("/" + servicesCodes[i] + "/TestDevice1.html", '',
done);
        }
    }
}


********END SCRIPT *************

However, when i run it, the result is as below :
The suprising thing is the order it is logged : it seems : as we go for
async, perhaps both request are started at the same time so each one get a
starting of 403 (no yet updated). Could this be the pb ? What could be the
solution ? Run subrequest without giving the done function as callback and
directly test the return status ?

2018/10/30 21:43:06 [warn] 9220#9220: *203 js: Variables init ...
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: n is :0
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: final_status is : 403 // This
is set by the variable declaration
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: servicesCodes is : rest,oass
// Loop to subrequest for each services
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: requestCount is : 2
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: Final status before sending
subrequest to next service is 403 // Start of the loop with rest :
final_status is still original value of 403
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: subrequest is on : rest // it
returns an HTTP/200 so final_status is configured to 200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: Final status before sending
subrequest to next service is 403 // However, here, final_status is seen as
being 403 so we still go over the loop for the second service oass even if
there is a break statement based on final_status == 200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: subrequest is on : oass
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: at start of done function, n
is :1
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: status of subrequest is :200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: lets set final_status to 200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: Value of final_status :200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: !!! We return 200 because we
have this one at least, no matter if other are 404 !!!
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: at start of done function, n
is :2
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: status of last subrequest is
:200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: lets set final_status for
last subrequest to 200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: Value of final_status after
last subrequest:200
2018/10/30 21:43:06 [warn] 9220#9220: *203 js: !!! We return the final 200
!!!

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281750#msg-281750

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

Valentin V. Bartenev-3
On Wednesday, 31 October 2018 00:55:20 MSK you wrote:
[..]

> However, when i run it, the result is as below :
> The suprising thing is the order it is logged : it seems : as we go for
> async, perhaps both request are started at the same time so each one get a
> starting of 403 (no yet updated). Could this be the pb ? What could be the
> solution ? Run subrequest without giving the done function as callback and
> directly test the return status ?
[..]

Subrequests are async.  That allows you to do a lot of interesting stuff,
like in this example where two subrequests are run in parallel:
http://nginx.org/en/docs/njs/examples.html#fast_response

If you want to schedule the second subrequest only after the first one is
finished, then simply put your second subrequest call inside the done
callback of the first one.

  wbr, Valentin V. Bartenev



_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

Maxim Konovalov
On 31/10/2018 01:27, Valentin V. Bartenev wrote:

> On Wednesday, 31 October 2018 00:55:20 MSK you wrote:
> [..]
>
>> However, when i run it, the result is as below :
>> The suprising thing is the order it is logged : it seems : as we go for
>> async, perhaps both request are started at the same time so each one get a
>> starting of 403 (no yet updated). Could this be the pb ? What could be the
>> solution ? Run subrequest without giving the done function as callback and
>> directly test the return status ?
> [..]
>
> Subrequests are async.  That allows you to do a lot of interesting stuff,
> like in this example where two subrequests are run in parallel:
> http://nginx.org/en/docs/njs/examples.html#fast_response
>
Some additional stuff to this topic

https://github.com/nginxinc/nginx-openid-connect

--
Maxim Konovalov
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

wld75
In reply to this post by Valentin V. Bartenev-3
My problem is that services can be one, two … ten etc … so not easy to place
in the callback of the previous subrequest ...

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281753#msg-281753

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

wld75
In reply to this post by Maxim Konovalov
Ok, got it !
Thanks much Valentin, Maxim and Dmitry for your guidance.

I decided to go as the nginx sample
(https://www.nginx.com/blog/batching-api-requests-nginx-plus-javascript-module/)
with the resp var which is concatened with result of each subrequest : my
final_status starts with 403 at init time and I add (+=) each subrequest
result so with two subrequest ending in 200, we endup with final_status =
403200200.

At the end of the loop, i call the evaluateStatus function (to make the
evaluation after all subrequests end) to test if final_status contains 200
and we set the res.return(200) or (403) based on that.

Here is the code (i still need to make some cleanup with logging). By the
way, do you have any advice on tuning for better perfs ?

var n = 0;
var final_status = '403';
var servicesCodes = ['rest','oass'];
var requestCount = servicesCodes.length;
   
function authorize(req, res) {
    req.warn('Variables init ...');
   
    function done(reply) { //Callback for completed subrequests ...
        n++;
            reply.warn('at start of done function, n is : ' + n);
        if (n < requestCount) { //| final_status !=200) {
                reply.warn('status of subrequest is : ' + reply.status);
            if (reply.status == 200) {
                    reply.warn('lets set final_status to 200');
                final_status += '200';
                reply.warn('Value of final_status : ' + final_status);
                // reply.warn('!!! We return 200 because we have this one at
least, no matter if other are 404 !!!');
                // res.return(200);
            }
        } else { // last response
            reply.warn('status of last subrequest is :' + reply.status);
            if (reply.status == 200) {
               reply.warn('lets set final_status for last subrequest to
200');
                final_status += '200';
                reply.warn('Value of final_status after last subrequest: ' +
final_status);
            } else { // we did not get any 200
                reply.warn('!!! We dont insert 200 as we dont have ... ');
                reply.warn('Value of final_status after last subrequest: ' +
final_status);
            }
            // Send the final result
            evaluateStatus(final_status);
        }  
       
        function evaluateStatus(status) {
            if (final_status.includes('200')) {
                    res.return(200)
            } else {
                    res.return(403)
            }
        }
    };



    req.warn('n is :' + n);
    req.warn('final_status is : ' + final_status);
    req.warn('servicesCodes is : ' + servicesCodes);
    req.warn('requestCount is : ' + requestCount);

    for (var i = 0; i < requestCount; i++) {
        req.warn('Entering loop for ' + servicesCodes[i])
        req.subrequest("/" + servicesCodes[i] + "/TestDevice1.html", '',
done);
    }

}

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,281699,281754#msg-281754

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: Variable scope in javascript module

Valentin V. Bartenev-3
In reply to this post by wld75
On Wednesday, 31 October 2018 01:47:49 MSK alweiss wrote:
> My problem is that services can be one, two … ten etc … so not easy to place
> in the callback of the previous subrequest ...
>

Actually it's not a problem.  Here's an example:

function authorize(r) {
    var n = 0;
    var svcs = ['one', 'two', 'three'];

    var callNextService = function() {

        function done(reply) {
            if (reply.status == 200) {
                r.return(200);
                return;
            }

            callNextService();
        }


        if (n == svcs.length) {
            r.return(403);
            return;
        }

        r.subrequest("/" + svcs[n++], '', done);
    }

    callNextService();
}

  wbr, Valentin V. Bartenev



_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx