in an `async` function are the input parameters preserved over an `await` - javascript

I have some browser code which has been showing some mysterious errors when running over a slow link. I am coming to the conclusion that the event.detail parameter is getting corrupted.
So I have an async function (as a method on a class) a bit like the following:
async doUpdate(e) {
e.stopPropagation();
const response = await readSomethingFromRemoteDatabase(this.id, e.detail.anotherid);
const myentity = this.entities.find(x => x.id === e.detail.anotherid)
if (typeof myentity !== 'undefined') {
//do something to update local data
}
}
I expect myentity to be found, but occasionally it isn't.
I have since changed the code to become the following:
async doUpdate(e) {
e.stopPropagation();
const data = e.detail
const response = await readSomethingFromRemoteDatabase(this.id, e.detail.anotherid);
const myentity = this.entities.find(x => x.id === data.anotherid)
if (typeof myentity !== 'undefined') {
//do something to update local data
}
}
and the issue has gone away.
I'm coming to the conclusion that with the slow link, its possible for doUpdate to get called twice, the second time before the readSomthingFromRemoteDatabase has responded, and the second call has come in with different data.
What that implies is that the const data is within the scope of the called function for the entire scope of its execution, but that the parameter variable e is not.
However, I can find no documented information about whether my assumption is true. Can someone point me to where I can find out if my assumption is true?
UPDATE in response to some of the comments.
Firstly, doUpdate is an event listener which is called via lit-element. Elsewhere in this class is a Lit render function, a bit like this
render() {
return html`
${this.entities.map(entity => html`
<entity-editor .entity=${entity} #entity-changed=${this.doUpdate}></entity-editor>
`)}
`;
}
The event detail is an object constructed from parts, so not a reference elsewhere. Normally made something like this (inside the entity-editor custom element.
this.dispatchEvent(new CustomEvent('entity-changed', {
bubbles: true,
composed: true,
detail: {
anotherid: this.id,
moreinfo: this.moreinfo
)
}));
readSomethingFromRemoteDatabase does little more than translate the object passed into a fetch POST request to the server, where the object passed is JSON.stringify'ed into the body of the request.
Sorry that I can't point you at real code - but this is a massive PUA I personally have been building since 2016 advancing through Polymer and now Lit to replace a Microsoft Access Application. It's private work for a company so is in a private repository.
At this stage it's 100s of elements in a client, and 100s of components in a Node.js api-server (proxied by NGINX)

Related

Is it unsafe to invoke a function by it's name as a string using the window object?

Would you please tell me if there is anything unsafe, in the JS of a standard web page, about invoking a function by it's name as a string using window['function_name']()? I remember reading something about it quite some time ago but cannot locate it now.
I thought there was a reason to not use the window object to do so, because to invoke functions between a background script and content script in a web extension, I always declare a different object in each and declare the functions that can be invoked in this manner as properties of the object and pass the function names as strings in the communication port to invoke them; but I cannot remember why.
Thank you.
It depends on which context your running the code,
1. JS Execution Context
Its fine to use string as function name and call the corresponding function residing in an object.
const functionName = "someFunction";
window[functionName]()
But If string is part of a untrusted data or user controllable string then it not safe to use. i.e Reading a string from a url parameter.
Example:
const unTrustedUserInput = window.location.hash
window[unTrustedUserInput]();
2. web Extension BG & CS Context
As per chrome recommendation, you should not trust the message received from content-script. You should always sanitise the input and place necessary validation before executing it.
So, I would recommend not to pass function name as string, instead use a dictionary map with corresponding guid to validate to which function the call is made.
Example:
// cs context
chrome.extension.sendMessage({ id: "<GUID-1>", data: "data" }, (response) => {
// handle response if any
});
// BG context
var CSBGCommunicationMap = {
"<GUID>-1": window.someFunction
}
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (sender.id !== "<varlidate id-ur-Extension>" && sender. origin !== "<validate origin>") {
// Early return if it comes from malicious content or unknown sender
return;
}
if (message.id && message.id in CSBGCommunicationMap) {
const functionToBeCalled = CSBGCommunicationMap[message.id];
// functionToBeCalled(message, sendResponse);
}
});
I hope this clarifies your concern.
According to MDN, The Window object is a global object which contains the entire DOM document. So if you call a function foo() (without specifying any object), Javascript will search it in window object.
In other hand,
foo() , window.foo() and window['foo']() are same. But when talk about security, Let's say if user injects some malicious code into the function foo,
doesn't matter you invoke the function foo() or window['foo'](), The injection will effect both.
Avoid using Window object
You don't need to specify the window object to call a global scoped functions or variable unless, it shadowed by your current scope.
function x() {
console.log('hey i am global');
}
function y() {
function x() {
console.log('I have power only inside y()');
}
x(); // I have power only inside y()
window.x() // hey i am global
}
And If you don't handle window object properly, There are lot of chances to get run-time errors and the entire object will be collapsed.

jQuery - not able to consistently access created DOM elements

I'm working with jQuery (version 3.2.1), and I'm finding that sometimes, for reasons that I cannot discern, jQuery is unable to locate jQuery-created DOM elements. I would say that this issue occurs about 1 out of every 10 times I refresh the page. In those instances, the element is undefined.
It's a bit of a long and involved script, so I'll attempt to distill it to its critical parts. First of all, the scripts are introduced in the index.html like so:
<body>
...
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="app.js"></script>
</body>
Pretty much what you'd expect. Here's the relevant (and abbreviated) code from app.js - the problem occurs in the loadItems() function:
function getQueryParamCat(queryParam) {
return $('.category-item[data-query_name=' + '\'' + queryParam + '\'' + ']');
}
function loadItems(queryParam) {
$.post('./get_items.php', {}, () => {
const queryParamCat = getQueryParamCat(queryParam);
if (queryParamCat[0]) {
// Leaving out categoryClick() - it triggers a click on the relevant DOM element
categoryClick(queryParamCat);
} else {
categoryClick($('category').first());
}
});
}
function loadCategories(callBack) {
$.post('./get_categories.php', {}, (data) => {
const categories = $.parseJSON(data);
$.each(categories, (i, value) => {
const cat = $('<category>').appendTo($('left')).html(value.name);
cat.attr('class', 'category-item');
cat.attr('data-query_name', value.name.toLowerCase());
cat.mousedown(function () {
categoryClick($(this));
});
});
return callBack;
});
}
$(document).ready(() => {
// Leaving out getParameterByName() - just gets a string from the url
const queryParam = getParameterByName();
loadCategories(loadItems(queryParam));
});
In brief summary:
the page loads and loadCategories() is called.
the client makes an AJAX request to get_categories.php, and the returned data is used to create a set of <category> DOM elements.
loadItems(queryParam) is then called as a callback, which then makes an additional AJAX request to get more data.
In the callback following that request, we ultimately want to call the categoryClick() function, passing in a <category> DOM element as the argument (the element to be 'clicked'). THIS IS WHERE THE PROBLEM OCCURS.
About 1 out of 10 times, the result of getQueryParamCat() comes back as r.fn.init [prevObject: r.fn.init(1)], which makes the value of queryParamCat[0] in the conditional in loadItems() evaluate to undefined. However, in those situations, $('category') also evaluates to r.fn.init [prevObject: r.fn.init(1)], meaning that $('category').first() is also undefined.
This problem only seems to affect elements that are created by jQuery - anything that was hard-coded in the HTML can be accessed, no problem. Why is it that jQuery is unable to consistently find these elements? Is it trying to find those elements before they've been successfully appended? I could understand if it failed all the time, but the inconsistency is confusing to me. Can anyone offer any suggestions as to how to make this code perform reliably?
Odd syntax; loadCategories expects a callback as an argument, but loadItems doesn't return anything, so loadCategories(loadItems(queryParam)); turns into loadCategories(undefined);.
Also, return callBack; doesn't do anything inside of a $.post function; it's not only not returning the value to the outer function's caller, it's also running async.
Maybe did you mean to do something like this?
loadCategories(() => {
loadItems(queryParam)
});
function loadCategories(callBack) {
// ...
$.each(categories, (i, value) => {
// ...
});
callBack();
That ensures the callback is called after the meat of loadCategories is done.

How to properly stub a function return value?

Premise: JS ES6, NodeJS
Testing Framework: TAP
Mocking Library: testdouble.js
I am attempting to mock the return value for the method of my class and keep receiving this error:
not ok Unsatisfied verification on test double. Wanted: - called with (true). But there were no invocations of the test double.
Here is my testing code:
// Imports for unit testing
const tap = require('tap');
const Subject = require('../src/iTunesClient.js');
const td = require('testdouble');
let reqJson;
// Ensure the iTunes class methods are called
tap.test('iTunesClient class methods function as intended', (t) => {
t.beforeEach((ready) => {
reqJson = td.replace('../src/reqJson.js');
ready();
});
t.afterEach((ready) => {
td.reset();
ready();
});
t.test('iTunesClient.getData', (assert) => {
const callback = td.function();
const subject = new Subject();
subject.setTerm('abc 123');
subject.setURL();
td.when(reqJson.get(td.callback)).thenCallback(true);
subject.getData(callback);
td.verify(callback(true));
assert.end();
});
t.end();
});
Specifically, this line is related to my issue:
td.verify(callback(true));
How can I fake the callback value of true for reqJson.get()? Right now, Subject.geData() is a method of the iTunesClient class which calls another file, reqJson.js, to use its exported get() method.
It's a little hard to tell from your example, but it looks like you're requiring iTunesClient before you call td.replace. In this case, the real reqJson module will be required and cached on line 3.
You need to call td.replace early enough to avoid this, e.g. in between requiring tap and iTunesClient.
I wanted to update this question, as I recently solved this issue. Essentially, I had two issues:
Account for both reqJson function parameters
Account for all callback return values
Per testdouble documentation for item 1:
When passed td.matchers.anything(), any invocation of that test double function will ignore that parameter when determining whether an invocation satisfies the stubbing.
Hence, I adjusted my line of code as follows:
Before: td.when(reqJson.get(td.callback)).thenCallback(true);
After: td.when(reqJson.get(td.matchers.anything(), td.callback)).thenCallback(null, null, null);

Typescript - Synchronous ajax call using Frame.js (Solution: JQueryDeferred)

This question isn't exactly typescript related but without the context it would be unclear why I would even require such behavior. Should be relatively straight forward to understand whether you know Typescript or not.
I have a dialog class implementation in Typescript that looks something like this (only showing relevant methods and fields):
class BaseDialog{
...
public dialogEl: JQuery;
public AjaxLoadContent(route: string) {
if (this.dialogEl !== undefined)
this.dialogEl.load(route);
return this;
}
public HtmlLoadContent(html: string) {
if (this.dialogEl !== undefined)
this.dialogEl.empty().html(html);
return this;
}
public Show() {
if (this.dialogEl !== undefined)
this.dialogEl.dialog("open");
}
...
}
I'm returning this from AjaxLoadContent() and HtmlLoadContent() so that I can chain a call to Show() as follows:
var dialog = new BaseDialog();
dialog.AjaxLoadContent("/Account/Login").Show(); //Ajax call
dialog.HtmlLoadContent(someHtml).Show(); //Load from variable, no ajax call
I find this chaining syntax very clean and logical so I want to stick with it, however, in the ajax scenario, Show() gets called before ajax load() completes so the dialog opens, then there is a delay before the content appears. I can't provide a callback to load() since I'd like to explicitly chain Show() onto the call instead of calling it internally...therefore, I need some kind of synchronous mechanism.
I'm now looking into Frame.js to accomplish this "synchronous" style without hanging the browser with something like $.ajaxSetup({async: false;}). Here is the answer I was hoping would work: https://stackoverflow.com/a/10365952
However, the following code still has the delay:
public AjaxLoadContent(route: string) {
if (this.dialogEl !== undefined){
var that = this;
Frame(function (next) {
$.get(route, next);
});
Frame(function (next, response) {
that.dialogEl.html(response); //Breakpoint 1
});
Frame.init();
return this; //Breakpoint 2
}
}
However this doesn't seem to work as Breakpoint 2 gets hit first despite the explicit control flow I've defined. The Show() call happens immediately after return this (therefore loading a blank dialog), then finally that.jQueryDialog.html(response) gets called from the second Frame, loading the content after the dialog has already been shown (therefore still a delay).
How can I accomplish this synchronous behavior?
This is exactly (IMO) what JQueryDeferred is for. You can use that for all this without needing to add another dependency on Frame.js. The easiest way to do this would be to return a JQueryPromise from each Async method, like so:
///<reference path="./jquery.d.ts">
class BaseDialog{
public dialogEl: JQuery;
public AjaxLoadContent(route: string):JQueryPromise {
var deferred = $.Deferred();
if (this.dialogEl !== undefined)
this.dialogEl.load(route)
.done(() => deferred.resolve())
.fail(() => deferred.reject());
return deferred.promise();
}
public HtmlLoadContent(html: string):void {
if (this.dialogEl !== undefined) {
this.dialogEl.empty().html(html);
}
public Show():void {
if (this.dialogEl !== undefined)
this.dialogEl.dialog("open");
}
}
var dialog = new BaseDialog();
dialog.AjaxLoadContent("something")
.done(() => dialog.Show());
That's not quite as clean an interface, but the alternative is to do some awfully clever coding whereby your class throws each Deferred into a FIFO queue, and each subsequent method waits on the previous Deferred in the queue before it starts executing. Certainly possible, and if you're designing this API for significant external consumption, it might be worth doing. But if you're just planning to use it for some internal project, it sounds like too much work and maintenance to me. (Just my opinion, of course :-).
(Other problems with your proposed interface: (1) it doesn't have any way of handling errors, analagous to the JQueryDeferred.fail() handler; and (2) it doesn't have any way of doing any external processing in-between calls to your class. What if you wanted to do a transform on the content before you called the Show() method?)
"However this doesn't seem to work as Breakpoint 2 gets hit first despite the explicit control flow"
Actually the flow control is working exactly as you have written it. Only things inside the Frame functions will be controlled by Frame. You can not use return statements inside of callbacks and expect them to return the calling function.
Ken's answer is correct that the use of jQuery Deferred will accomplish the same goal as Frame does in your above example. Frame is designed for sequences much longer than the one you have created. Either will behave the same way, the major difference is syntax.
Honestly, I think the delay you are experiencing is the time it takes to make the AJAX call. Maybe I am not understanding your question, but the Frame part looks right. Here are a few notes:
public AjaxLoadContent(route: string) {
if (this.dialogEl !== undefined){
var that = this;
Frame(function (next) {
$.get(route, next); // great!
});
Frame(function (next, response) { // good use of passing variables!
that.dialogEl.html(response); // yep, will happen synchronously!
// return that; // unfortunately, this would only return 'that'
// to Frame, not to AjaxLoadContent.
// It's not possible to return the calling function
// from inside a callback.
next(); // the callback should be called here
// to complete the Frame sequence.
});
Frame.init();
return this; // notice that the return statement here is not in Frame?
}
}

Usage of Observable pattern in JavaScript

function Observer() {
this.fns = [];
}
Observer.prototype = {
subscribe : function(fn) {
this.fns.push(fn);
},
unsubscribe : function(fn) {
this.fns = this.fns.filter(
function(el) {
if ( el !== fn ) {
return el;
}
}
);
},
fire : function(o, thisObj) {
var scope = thisObj || window;
this.fns.forEach(
function(el) {
el.call(scope, o);
}
);
}
};
var fn = function() {};
var o = new Observer;
o.subscribe(fn);
o.fire('here is my data');
o.unsubscribe(fn);
I am not able to understand the whole concept behind this. I want to implement this pattern in my project. I have a view where the form gets submitted and it calls an WebService and returns me response.
If i have to implement this in my project where this a simple request and response... how would i go about with it? i understand you notify your observer when there is a change... let's take i make a request to my API and i get the response back... now i want it to get notified to my view back through observable pattern
Observer appears to be a constructor that you call with var o = new Observer(); then o will be an object with a reference to a bunch of functions. you add functions to the list via subscribe. and remove them from the list via unsubscribe
then the whole point of it all is the "fire" method which will loop through the function list then call each of the functions one by one . "observer pattern" appears to be a lot like the singleton pattern
Are you familiar with the "watch" method in JavaScript? its a method supported via Firefox that you can use on any object.
document.myform.myfield.watch('value', function (v) {
alert(v);
return v;
})
then whenever the value of the object changes, the watch function is called. so basically the concept behind the observer pattern is that you want to basically simulate Firefox's watch method in a cross-browser fashion
you toss a reference to a bunch of functions or objects into subscribed list.then have Observer.fire call a callback method on each of the watched objects or functions. that way if the user preforms some sort of action such as clicking, then the whole list of functions would be updated via a callback function
I hope this helps.
If you only want to do a simple request then in jQuery (such as with $.ajax(...) or $.get(...)) that would look like this:
var requestUrl = "text.html";
// Callback is defined here
var viewCallback = function(data) {
// this will be called when the request is done
console.log('view is notified');
console.log('data looks like this:');
console.log(data);
// you could chain method calls to other callbacks here if you'd like
};
// Request is done here
$.ajax({
url: requestUrl,
}).done(viewCallback);
Most of the time you only want to do one thing when doing a request for which the above is enough code. Using javascript libraries such as jQuery or mootools will abstract away the oddities with the XMLHttpRequest object.
However if you want to do something much more advanced I'd recommend you look at libraries that do this sort of thing such as Radio.js.

Categories