I have a number of event handlers in my page that were accessing global functions (functions defined in Script tags on the page). For instance:
<button id="ClearText" onclick="cleartb()">Clear Text Box</button>
That cleartb() function simply sits on the page:
<script>
function cleartb()
{
vm.essayText('');
return;
}
</script>
Now, vm is my page's view model (but for this question, all that matters is that it was simply a global variable available to the entire page) and I use functions and values it exposes in several event handlers, alert messages, etc.
The problem is that I've moved the definition of vm into a RequireJS AMD module called vm.js:
define(["knockout", "jquery"], function (ko, $) {
var essayText = 'Hello World!';
...
return {
essayText: essayText
}
});
When my onlick event handler runs or I refer to vm in any manner, I get a "vm undefined" error (as expected).
Question 1:
How can I give my page access to the vm variable defined in an AMD module especially if I don't want to "pollute" the global namespace? Is there a best-practice here?
Question 2:
Ultimately, I don't even want cleartb() on the page because it really is a view-model-specific operation. Although I think I can figure out what to do once I have the (an?) answer to Question 1, I would be interested to know how best to move the cleartb function into the vm AMD module so that I still can call it from my onlick event handler.
Note that I want values and function still to be exposed from a vm variable so that I can continue to use vm.cleartb() or inspect the value of vm.essayText() (it's a KO observable). (In other words, I don't want to solve the problem with a cleartb(vm) solution.)
Thank you for any help!
<script>
function cleartb()
{
vm.essayText('');
return;
}
alert(window.cleartb);
</script>
Actually, this way is already pollute the global window variable. So I think your first requirement don't make sense. And then you can do this way:
define(["knockout", "jquery"], function (ko, $) {
var essayText = 'Hello World!', varToBeExported;
...
window.varToBeExported = {
'cleartb': cleartb
};
return {
essayText: essayText
}
});
But if unnecessary, you should using requireJs way - require(['your moudle'],.... .
Related
We are in the process of embedding JS in our application, and we will use a few dozen scripts each assigned to an event. Inside these scripts we provide a minimal callback api,
function onevent(value)
{ // user javascript code here
}
which is called whenever that event happens. The scripts have to have their own global, since this funtion has always the same name and we access it from cpp code with
duk_get_global_string(js_context_duk, "onevent");
duk_push_number(js_context_duk, val);
if (duk_pcall(js_context_duk, 1) != 0)
{
printf("Duk error: %s\n", duk_safe_to_string(js_context_duk, -1));
}
duk_pop(js_context_duk); /* ignore result */
Then again we want to allow minimal communication between scripts, e.g.
Script 1
var a = 1;
function onevent(val)
{
log(a);
}
Script 2
function onevent(val)
{
a++;
}
Is there a way we achieve this? Maybe by introducing an own 'ueber-' global object, that is defined once and referencable everywhere? It should be possible to add properties to this 'ueber-global object' from any script like
Script 1
function onevent(val)
{
log(ueber.a);
}
Script 2
function onevent(val)
{
ueber.a=1;
}
Instead of simple JS files you could use modules. duktape comes with a code example to implement a module system (including its code isolation) like in Node.js. Having that in place you can export variables that should be sharable.
We have an approach that seems to work now. After creating the new context with
duk_push_thread_new_globalenv(master_ctx);
new_ctx = duk_require_context(master_ctx, -1);
duk_copy_element_reference(master_ctx, new_ctx, "ueber");
we issue this call sequence in for all properties/objects/functions created in the main context:
void duk_copy_element_reference(duk_context* src, duk_context* dst, const char* element)
{
duk_get_global_string(src, element);
duk_require_stack(dst, 1);
duk_xcopy_top(dst, src, 1);
duk_put_global_string(dst, element);
}
It seems to work (because everything is in the same heap and all is single threaded). Maybe someone with deeper insight into duktape can comment on this? Is this a feasible solution with no side effects?
edit: mark this as answer. works as expected, no memory leaks or other issues.
I'm reasonably new to programming in nodejs but not to programming (C/C++/Python/Shaders) and I have a question about exclusive access to a global variable when e.g. async.mapLimit return its callbacks
example
var myGlobalCounter = 0;
function executeDownload(item, callback){
exec('./ascriptthatdownload.sh ' + item, function (error, stdout, stderr) {
// Here do I have exclusive access to myGlobalCounter
// so that I could do this or update lets say a UI component?
myGlobalCounter++
console.log('Downloads ready:', myGlobalCounter);
});
}
function downloadSomeFiles() {
var listOfFiles = [];
// create download links
async.mapLimit(listOfFiles, 4, executeDownload, function(err, results){
});
}
I can get this to work but I don't know if this is safe enough? Other suggestions also appreciated. In C/C++ I would have used a mutex to guard against simultaneous access to myGlobalCounter.
Edit:I want to be able to safely count myGlobalCounter by 1 each time a download is ready and then pass it on either in console.log or to another component
The kudos goes to #CertainPerformance explanation in his comment (that I can't accept as an answer)
Yes, Javascript is single-threaded - a synchronous block of code will
run to the end before any other callback can run, look up the event
loop. You almost never have to worry about shared mutable state in JS
I would like to save the initial state of a workbook (or an Excel application) in the beginning, so that I could always get back to it regardless of modification on the workbook by my Add-in.
I tried some following code in Home.js:
(function() {
"use strict";
Office.initialize = function(reason) {
$(document).ready(function() {
app.initialize();
initial();
$('#getInitial').click(getInitial);
});
};
var ctxInitial;
function initial () {
ctxInitial = new Excel.RequestContext();
}
function getInitial() {
Excel.run(function () {
var wSheetName = 'Sheet1';
var worksheet = ctxInitial.workbook.worksheets.getItem(wSheetName);
var usedRange = worksheet.getUsedRange();
usedRange.load(["values"]);
return ctxInitial.sync().then(function() {
document.getElementById("area").value += usedRange.values.toString();
});
});
}
})();
In the very beginning, my tests prints well the initial values of the worksheet. However, after some manual modification on some cell values, getInitial prints the current state of the worksheet rather than the initial values.
Does anyone know what's the best practice to realise this?
A context does not store workbook state (How could it? It's a JavaScript object that lives in a completely separate world from the document). A context is merely a pipeline (or, if you will, an accumulator of commands) of what actions to dispatch.
There is no real reason to hang on to a context object, except for using it as a way of creating two objects from the same context (e.g., range1.getIntersection(range2), since objects must be from the same context in order to interact). But beyond that, the context's life can (and generally should) be as quick as possible. That's why in Excel.run we always create a new context for you, and dispose of it at the end.
On a related note, and for the same reasoning, it makes no sense to do an Excel.run and NOT use the context that it provides (or use a different context, as you do in your example). You could just as easily run your code without an Excel.run, it gains you nothing to have it be in an Excel.run block if you're reusing an existing context (and note that you won't get the automatic object-tracking that you would have with a clean Excel.run).
Hope this helps!
~ Michael Zlatkovsky, developer on Office Extensibility team, MSFT
Background
I am working on a C# program which currently runs Node via Process.Start(). I am capturing the stdout and stderr from this child process and redirecting it for my own reasons. I am looking into replacing the invocation of Node.exe with a call to Edge.js instead. In order to be able to do this I must be able to reliably capture stdout and stderr from the Javascript running within Edge, and get the messages back into my C# application.
Approach 1
I'll describe this approach for completeness in case anybody recommends it :)
If the Edge process terminates, it is fairly easy to deal with this by simply declaring a msgs array and overwriting process.stdout.write and process.stderr.write with new functions that accumulate messages on that array, then at the end, simply return the msgs array. Example:
var msgs = [];
process.stdout.write = function (string) {
msgs.push({ stream: 'o', message : string });
};
process.stderr.write = function (string) {
msgs.push({ stream: 'e', message: string });
};
// Return to caller.
var result = { messages: msgs; ...other stuff... };
callback(null, result);
Obviously this only works if the Edge code terminates, and msgs may grow large in the worst case. However, it is likely to perform well because only one marshalling call is necessary to get all the messages back.
Approach 2
This is a little harder to explain. Instead of accumulating messages, we "hook" stdout and stderr using a delegate we send in from C#. In the C#, we create an object that we will pass into Edge, and that object has a property called stdoutHook:
dynamic payload = new ExpandoObject();
payload.stdoutHook = GetStdoutHook();
public Func<object, Task<object>> GetStdoutHook()
{
Func<object, Task<object>> hook = (message) =>
{
TheLogger.LogMessage((message as string).Trim());
return Task.FromResult<object>(null);
};
return hook;
}
I could really get away with an Action, but Edge appears to require the Func<object, Task<object>>, it won't proxy the function otherwise. Then, in the Javascript, we can detect that function and use it like this:
var func = Edge.Func(#"
return function(payload, callback) {
if (typeof (payload.stdoutHook) === 'function') {
process.stdout.write = payload.stdoutHook;
}
// do lots of stuff while stdout and stderr are hooked...
var what = require('whatever');
what.futz();
// terminate.
callback(null, result);
}");
dynamic result = func(payload).Result;
Questions
Q1. Both of these techniques seem to work, but is there a better way of doing this, something built-in to Edge perhaps that I have missed? Both solutions are invasive - they require some shim code to wrap the actual work that is to be done in Edge. This is not the end of the world, but it would be better if there was a non-invasive method.
Q2. In approach 2, where I have to return a task here
return Task.FromResult<object>(null);
it feels wrong to be returning an already completed "null task". But is there another way of writing this?
Q3. Do I need to be more rigorous in the Javascript code when hooking stdout and stderr? I note in double-edge.js there is this code, frankly I am not sure what is happening here, but it is quite a bit more complex than my crude overwriting of process.stdout.write :-)
// Fix #176 for GUI applications on Windows
try {
var stdout = process.stdout;
}
catch (e) {
// This is a Windows GUI application without stdout and stderr defined.
// Define process.stdout and process.stderr so that all output is discarded.
(function () {
var stream = require('stream');
var NullStream = function (o) {
stream.Writable.call(this);
this._write = function (c, e, cb) { cb && cb(); };
}
require('util').inherits(NullStream, stream.Writable);
var nullStream = new NullStream();
process.__defineGetter__('stdout', function () { return nullStream; });
process.__defineGetter__('stderr', function () { return nullStream; });
})();
}
Q1: There isn't anything built into Edge that would make capturing stdout or stderr of Node.js code automatic when calling Node from CLR. At some point I thought of writing an extension of Edge that would make marshaling Streams across CLR/V8 boundary easy. Under the hood it would be very similar to your Approach 2. It could be done as a standalone module on top of Edge.
Q2: Returning a completed task is very appropriate in this case. Your function has captured the Node.js output, processed it, and has in fact "completed" in that sense. Returning a task completed with Null is really a moral equivalent of returning from an Action.
Q3: The code you are pointing to is only relevant in Windows GUI applications, not Console applications. If you are writing a Console application, simply overriding write should suffice at the level of the Node.js code you pass to Edge.js. Note that the signature of write in Node allows an optional encoding parameter to be passed in. You seem to ignore it both in Approach 1 and 2. In particular in Approach 2 I would suggest wrapping the JavaScript proxy to C# callback into a JavaScript function that normalizes the parameters before assigning it to process.stdout.write. Otherwise Edge.js code may assume that the encoding parameter passed to a write call is a callback function which would follow the Edge.js calling convention.
I need one simple thing:
var Base = function(module){
this.outsideMethod = function(arg1)
{
// run method in new context - sandbox
return vm.runInNewContext(module.insideMethod, arg1);
}
}
is something like this possible in nodejs? thx very much
If the insideMethod function does not call or use functions/vlues from outside the context it shall run in, yes.
You can convert any function in Javascript to a string.
Doing vm.runInNewContext('('+module.insideMethod+')('+JSON.stringify(arg1)+"); could be what you want.