Use function from .then method of Promise outside its scope - javascript

I have a file called paintTiming.js, which uses the Paint Timing API for finding out Web Performance paramters such as First Paint and First Contextual Paint. The file contents are shown below:
var FP, FCP, obj = [];
function parent() {
if ("PerformanceObserver" in window) {
let observerPromise = new Promise((resolve, reject) => {
// access the PerformanceObserver interface
let observer = new PerformanceObserver((list) => {
resolve(list);
});
observer.observe({
entryTypes: ["paint"]
});
}).then((list) => {
// Find out First Paint and First Contextual Paint
FP = list.getEntries()[0].startTime;
FCP = list.getEntries()[1].startTime;
// Store in array
obj[0] = FP;
obj[1] = FCP;
element = "The paint times are: <br> First Paint : " + FP + "ms, <br> First Contentful Paint : " + FCP + "ms";
// show values on web page
document.getElementsByTagName('p')[1].innerHTML = element;
}).then(() => {
// check if array is created, and is functioning
console.log(obj[0]);
}).then(() => {
// create function
function abc() {
return {
a: obj[0],
b: obj[1]
};
};
}).catch((error) => {
console.warn(error);
});
}
};
As you can see, a function abc() should be created when the promise is resolved.
My question : How do I use this function outside the promise?
For example, consider the sample below (used in a HTML file):
<script type="text/javascript" src="paintTiming.js"></script>
<script type="text/javascript">
// should return object containing FP and FCP values
console.log(abc());
</script>
The console shows that the function is not defined. If the JS code is inline, it still doesn't work.

You may not able to use the function if this is declared inside .then. Because this function abc is private to the callback function passed to .then & .then will only execute with the ajax.
Alternatively you can define this function outside the Promise and call this function inside the .then callback. Also need to bind the context using this

Related

Converting a string to a function in JavaScript

I am playing around with some office JavaScript and attempting to create an executable function from a string that is received from an API call.
The office JavaScript task pane for Excel makes a call to an external API on button click, and returns a function in the form of a String object. To make this into a function object, I have used:
var executable = new Function(response)
executable();
Unfortunately, nothing is happening, it doesn't seem to be calling the function at all.
After some debugging, I believe the reason it isn't getting called is because the response string object is already a full function, and new Function() is wrapping the response in another layer of function.
Response is:
async function highlightCells() {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sheet1");
const range = sheet.getRange();
range.format.fill.color = "yellow";
await context.sync();
console.log("Called");
});
}
And executable is resolving to:
function anonymous() {
async function highlightCells() {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sheet1");
const range = sheet.getRange();
range.format.fill.color = "yellow";
await context.sync();
console.log("Called");
});
}
}
Any ideas how to prevent the additional function wrapper from appearing? As you can see the response object is already a full function.
Do I need to use some other method of converting the string to a function or is there a way to override the wrapper within the new Function() syntax?
If you don't know the function name in advance, you can wrap the function definition in brackets to call it.
let response = `async function test() {
console.log("function called");
}`;
let executable = new Function(`(${response})();`);
executable();
If you need to pass it arguments or await it, make it return the function and call the function to get your actual function.
let func = `async function sum(a,b) {
return new Promise(resolve => setTimeout(() => resolve(a+b), 1000));
}`;
let executable = new Function(`return ${func};`)();
(async () => {
let val = await executable(3,4);
console.log("the sum is", val);
})();
If you know that it is guaranteed to be a function you could directly invoke it in the Function:
let data = 'function(arg1, arg2) { return arg1 + " " + arg2 }'
let func = new Function(`return (${data}).apply(this, arguments)`)
console.log(func(1,2))
With .apply(this, arguments) you call that function and pass the arguments you pass to your Function object into the received function. And the return returns the result that function.
Passing this to apply ensures that the Function object could be stored in an object and that the function your received could access that object using this. This might not be required, but makes the function behave like a regular function:
let data = 'function(arg1, arg2) { return arg1 + " " + arg2 + " " + this.prop }'
let obj = {prop : 'somevalue'}
obj.func = new Function(`return (${data}).apply(this, arguments)`)
console.log(obj.func(1, 2))
Simply use eval instead of new Function. You need to force the code to be an expression, not a statement, that's what the 0, part is for.
code = `function test() {
console.log('hey!')
}`
let executable = eval('0,' + code)
executable()
var executable = new Function(response + ';highlightCells()')
executable();
Since the returned code is a complete executable statement, use eval() to execute it, not new Function().
eval(response);
highlightCells();
Note that this requires that you know the name of the function that's being defined in the response. If not, you need to write code that parses it to extract the function name.

How to retrieve value from process.on in nodejs?

The current code that I am using:
async function MainFunction(){
let arrOO;
process.on('message', (jData) => {
let sName;
if(jData.koota){
sName = jData.koota[0]
}
console.log(sName + ' hello!')
arrOO = sName
})
console.log(arrOO + ' calling outside of .on')
}
I am attempting to print jData.koota[0] which is assigned to sName so that it can used in the whole function. In this case, outside of the process.on, I want to print it. When this code is run, 'undefined' is returned at console.log(arrOO + ' calling outside of .on'). How would I call sName outside of process.on?
process.on() just installs an event listener and immediately returns, so your console.log() runs BEFORE any events have happened. process.on() is referred to as non-blocking. That means it does some initial work and then immediately returns and the actual data arrives sometime later when it calls your callback.
So, your console.log() is just attempting to look at the data before any data has been put in there. Note that most of the time when you're trying to assign to a higher scoped variable from an asynchronous callback function, that's a likely sign of a coding error because code at the higher scope won't have any idea when to access that data.
For any code that wants to know about those process messages, you need to either put that code inside the process.on() callback or put it in a function that you call from inside that callback. That's the ONLY way you will know when an incoming message has occurredl and when some data is available.
Here's one option:
function MainFunction(){
process.on('message', (jData) => {
let sName;
if(jData.koota){
sName = jData.koota[0];
// call some function and pass it the new data
newDataArrived(sName)
}
});
}
// this function gets called when we have a new sName
function newDataArrived(sName) {
// put code here that uses the data
console.log(sName);
}
Or, you can make it a promise based interface that resolves a promise on the next matching event you get:
function MainFunction() {
return new Promise(resolve => {
function messageHandler(jData) {
if (jData.koota) {
resolve(jData.koota);
// remove message listener so they don't pile up
// since a promise is a one-shot device, we can
// only use this promise once
process.off('message', messageHandler);
}
}
process.on('message', messageHandler);
});
}
Mainfunction().then(sName => {
// use sName here
});

Use closure to purify a function which builds an object through recursion — JavaScript

I've made a promise based function which crawls up a hierarchy until it reaches the top, and resolves with an object containing the structure. My only gripe with the code is that I modify variables outside the function body, meaning that it is not a pure function. I've looked into JavaScript closures, and I fully grasp trivial uses of them. But I'm struggling to figure out how/if they can help make my function pure. My attempts at making a closure so far have only overwritten the variables, not modified them. Here is the code in question using global variables:
/* I want to move these variables inside function body to purify 'getPriorRows'*/
let priorRows = {}, level = 0;
const getPriorRows = id => new Promise(resolve => {
fetch(`/api/org/${id}`).then(result => {
/* global varaiables are modified here */
priorRows[level++] = result;
if (result.parentID) resolve(getPriorRows(result.parentID));
else resolve(priorRows);
});
});
getPriorRows('123432').then(result => console.log(result));
Any input on the matter is greatly appreciated.
Pass the values as arguments:
function getPriorRows(id, priorRows = {}, level = 0) {
return fetch(`/api/org/${id}`).then(result => {
/* global varaiables are modified here */
priorRows[level] = result;
if (result.parentID) return getPriorRows(result.parentID, priorRows, level+1);
else return priorRows;
});
}
getPriorRows('123432').then(result => console.log(result));
You can use either default parameters or a wrapper function, you don't even need a closure:
function getAll(id) { return getPriorRows(id, {}, 0); }
Also the I removed the Promise constructor antipattern.
You should be able to enclose the entire function and its "external" variables in a new function:
function getPriorRows(id) {
let priorRows = {}, level = 0;
const getNext = id => new Promise(
...
);
return getNext(id);
}
That said, your creation of an explicit new Promise in each iteration is a Promise anti-pattern:
function getPriorRows(id) {
let priorRows = {}, level = 0;
const getNext = id => fetch(`/api/org/${id}`).then(result => {
priorRows[level++] = result
if (result.parentID) {
return getNext(result.parentID));
} else {
return priorRows;
}
});
return getNext(id);
}
Either way, the advantage of wrapping the state like this is that you could now have multiple calls to getPriorRows proceeding in parallel without interfering with each other.
EDIT second code edited to fix a copy&paste error with the recursion - you must call the inner function recursively, not the outer one.

How should I pass data between small functions - via closures or via properties of an object?

I have a complex buisness action for example deleting a user account. It contains multiple connected steps, and has to keep track of some state between steps. What is a better approach for writing this action?
I see a lot of more functional approach like one below.
function someAction(someParam, anotherParam, callback) {
async.waterfall([
step1,
step2,
step3,
step4
],callback
);
function step1(p,cb){/**use someParam and anotherParam here via closure*/}
function step2(p,cb){/**...*/}
function step3(p,cb){/**...*/}
function step4(p,cb){/**...*/}
};
someAction('value', 1241, (err)=>{/**...*/});
What I don't like about this approach is that everything is defined within the scope of a single function (here someAction).
I find a more object-oriented way to be a little more readable. The state and the stepX functions are not truly private - sometimes it is convenient for testing.
function SomeAction(someParam, anotherParam){
//private state
this._someParam = someParam;
this._anotherParam = anotherParam;
};
SomeAction.prototype._step1 = function(p, cb){
//use this._someParam and this._anotherParam
};
SomeAction.prototype._step2 = function(p, cb){
//use this._someParam and this._anotherParam
};
SomeAction.prototype._step3 = function(p, cb){
//use this._someParam and this._anotherParam
};
SomeAction.prototype._step4 = function(p, cb){
//use this._someParam and this._anotherParam
};
//public api
SomeAction.prototype.execute = function(callback) {
async.waterfall([
this._step1,
this._step2,
this._step3,
this._step4
],callback
)
};
new SomeAction('value', 1241).execute((err)=>{/**...*/})
Is there any performance difference between them ? What is the recommended approach in Node.js ? Is it true that each time I callsomeAction in functional approach - all the stepX functions have to be defined from scratch ?
You could create curried versions of your step functions and stick them into the waterfall.
function step1(arg1, arg2, cb){
// Fuction body...
}
// other steps would be defined here...
function step4(arg1, cb){
// fuction body...
}
curried_step1 = step1.bind(undefined, 'junk', 'garb');
// Other steps curried here...
curried_step4 = step4.bind(undefined, 'something');
async.waterfall([
curried_step1,
curried_step2,
curried_step3,
curried_step4
],callback
);
Another approach could be to wrap up your data and state into an object (in place of a real monad) and use that object to pass around what you need.
You can use Promises, for a style that looks like so:
var Promise = require('promise');
//** Execute Program **//
main();
/**
* Do your async logic flow here
*/
function main() {
step1()
.then(step2) //Wait for step1 to finish, and pass the response directly to step2
.then(function(res) {
// Do something with res (the return from step2)
// Async execute step3 & step4
var promises = [
step3(),
step4()
];
// Wait for step3 & step4 to finish:
Promise.all([promises[0], promises[1]]).then(function(values) {
console.log(values[0]); //Value from step3
console.log(values[1]); //Value from step4
}).catch(function(e) {
console.log(e); //Reject or thrown errors from steps3 or 4
});
}).catch(function(e) {
console.log(e); //Reject or thrown errors from steps1 or 2
});
}
function step1() {
return new Promise(resolve, reject) {
//some async task here
resolve('step1');
//reject('step1'); //Or trigger a .catch (i.e. this function failed)
});
}
function step2() {
return new Promise(resolve, reject) {
//some async task here
resolve('step2');
//reject('step2'); //Or trigger a .catch (i.e. this function failed)
});
}
function step3() {
return new Promise(resolve, reject) {
//some async task here
resolve('step3');
//reject('step3'); //Or trigger a .catch (i.e. this function failed)
});
}
function step4() {
return new Promise(resolve, reject) {
//some async task here
resolve('step4');
//reject('step4'); //Or trigger a .catch (i.e. this function failed)
});
}
This is not quite an answer but answers your indirect question:
The question is how should I pass data between those small functions - via closures or via properties of an object.
There's a third way. If you are familiar with OO design then you might be familiar with the concept of the command pattern. That is, you need to construct a function dynamically but that's not possible so you create an object with one method that you can then customise based on the object's properties.
In functional programming, this design pattern is equivalent to the function factory pattern. Basically you write a function to generate another function.
So, you want to pass someParam and anotherParam to the async function but you want to be able to write that function outside of the someAction function. Here's how you can do it:
function someAction (someParam, anotherParam, callback) {
async.waterfall([
make_step1(someParam,anotherParam),
make_step2(someParam,anotherParam)
/* ... */
],callback
);
}
function make_step1 (someParam, anotherParam) {
return function (p, cb) {
// use someParam and anotherParam here
}
}
function make_step2 (someParam, anotherParam) {
return function (p, cb) {
// use someParam and anotherParam here
}
}
// ...
This removes the main objection you raised with the functional code: you no longer have to define all step functions inside someAction(), which makes it look more similar to the OO code.
This still does create new instances of all the step functions (only now returned from the make functions) every time you call someAction(). But the interpreter won't have to compile the functions again. Instead only a new closure is created (think of a closure as a frozen stack frame that is unlinked from the program stack).

Bluebird Promise Scope

I have just started using promises in attempt to cleanup some 'callback hell'.
I've decided on trying bluebird and I am running it in the browser but immediately ran into scoping problems.
Is there a way of setting the thisArg in a new Promise? The below example shows that the 'this' value inside the promise resolver is set to the browser window, but I'd like it set to the surrounding scope so I can easily access member variables.
I noticed there is a .bind() method but it only scopes the 'then()' method, not the promise. I also realize I can have 'var me = this' just before the promise and use closure, but I wanted to avoid it if possible.
function MyObject() {
this.value = 7;
}
MyObject.prototype.getValue = function () {
return new Promise(function (resolve) {
// some request/processing that takes a long time
var result = Ajax.request(...);
resolve({
value: this.value,
result: result
});
// 'this' is set to window instead of the instance,
// resulting in this.value as undefined
});
}
var obj = new MyObject();
obj.getValue().then(function (value) {
console.log(value); // preferably outputs 7
})
No, there is not. You can of course use the default approaches, but you shouldn't need to.
When doing heavy processing and getting back the value asynchronously, you want to get a promise for the value. You don't need to set the result value as a property of the original instance.
MyObject.prototype.getValue = function () {
return new Promise(function(resolve) {
// lots of processing to make a `value`
resolve(value); // no `this` at all!
});
};
In case you want to synchronously get the .value that you had set on the instance, you don't need the Promise constructor. Just use Promise.resolve to make a promise for an existing value:
MyObject.prototype.getValue = function () {
// TODO: lots of processing
return Promise.resolve(this.value);
};
Or, in your case, even Promise.method:
// TODO: lots of processing
MyObject.prototype.getValue = Promise.method(function () {
return this.value;
});
This is more a comment then an answer, as it is primary opinion based.
In the rar situations where I need this it would look like this in my code:
Ajax.requestAsync in this case would be a promisifyed version of Ajax.request.
I know this might just move your problem to another place.
MyObject.prototype.getValue = function () {
return Ajax.requestAsync(...)
.bind(this)
.then(function(result) {
return {
value: this.value,
result: result
}
});
}
Or something like this:
MyObject.prototype.getValue = function () {
var returnValue = {
value: this.value
};
return Ajax.requestAsync(...)
.then(function(result) {
returnValue.result = result;
return returnValue;
});
}
A rarely use such constructs:
MyObject.prototype.getValue = function () {
return Promise.all([this, Ajax.requestAsync(...)])
.spread(function(object, result) {
return {
value: object.value,
result: result
};
});
}

Categories