How to pass multiple functions/ objects to javascript classical constructor - javascript

I have the following code:
class simplePromise {
constructor(resolveFn, rejectFn) {
console.log(resolveFn, 'resolveFn') // (resolutionFunc, rejectionFunc) => {
// resolutionFunc(777);
// rejectionFunc();
// }
console.log(rejectFn, 'rejectFn') //undefined
}
}
const promise1 = new simplePromise( (resolutionFunc, rejectionFunc) => {
resolutionFunc(777);
rejectionFunc();
});
As you can see, I'm trying to pass in 2 functions to the constructor. However, when I console.logged each of them out, I noticed that both functions are registered as 1 argument. In this case, how do I separate the 2 functions?

Are you sure that you want to pass 2 functions to the constructor?
const promise1 = new simplePromise( (resolutionFunc, rejectionFunc) => {...}
this part of your code looks more like you want a single callback function that recieves 2 functions.
Like this:
class SimplePromise {
constructor(callback) {
const resolveFn = (value) => {
console.log("resolved with value", value);
};
const rejectFn = (error) => {
console.log("rejected with error", error);
}
callback(resolveFn, rejectFn);
}
}
const promise1 = new SimplePromise((resolutionFunc, rejectionFunc) => {
console.log('resolutionFunc', resolutionFunc)
console.log('rejectionFunc', rejectionFunc) //undefined
resolutionFunc(777);
rejectionFunc();
});

Not that this code has anything to do with a Promise, but you should study the following structure:
class simplePromise{
constructor(resolveFn, rejectFn){
this.prop1 = 'prop1value'; this.prop2 = 'prop2value';
resolveFn.call(this, 'Passing to first function argument'); // calling in simplePromise context
rejectFn.call(this, 'Passing to second function argument');
}
}
const promise1 = new simplePromise(function(funcOneArg){
console.log(funcOneArg);;
console.log("this.prop1 = '"+this.prop1+"';"); // see why `.call(this` is in `constructor`
console.log("this.prop2 = '"+this.prop2+"';");
},
function(functionTwoArg){
console.log('-'.repeat(45));
console.log(functionTwoArg);
});

So when you pass the arrow function to the new simplePromise constructor, that entire function is the first argument (resolveFn). In order to instantiate this the way you want, you need to define the resolutionFunc and rejectionFunc and then do it like so:
const resolutionFunc = () => {do stuff}
const rejectionFunc = () => {do stuff}
const promise1 = new simplePromise(resolutionFunc, rejectionFunc)

Related

get value from a Promise for unit-testing

I have a method inside a class calculating the sum of 2 parameters that returns a Promise (a must):
module.exports = class Sum {
sum(a, b) {
let c = a + b;
const myPromise = new Promise(function(myResolve) {
setTimeout(function(){ myResolve(c); }, 100);
});
return myPromise;
}
}
I use Jasmine framework to unit-test
const MyLocalVariable1 = require('../src/sum');
describe('CommonJS modules', () => {
it('should import whole module using module.exports = sum;', async () => {
const result = new MyLocalVariable1();
expect(result.sum(4, 5).then(function(value) {
return value;
})).toBe(9);
});
}
The first expression is what we want to test:
result.sum(4, 5).then(function(value) {
return value;
})
and the second one is the expected value:
toBe(9)
But how can I get a value from the first expression, because it's a Promise, and expected value of it is [object Promise]. Thanks in advance
To drive Konrad's point home, you can do the following:
it('should import whole module using module.exports = sum;', async () => {
const result = new MyLocalVariable1();
const answer = await result.sum(4, 5);
expect(answer).toBe(9);
});
Or the following:
// add done callback to tell Jasmine when you're done with the unit test
it('should import whole module using module.exports = sum;', (done) => {
const result = new MyLocalVariable1();
result.sum(4, 5).then(answer => {
expect(answer).toBe(9);
done();
});
});

How can I achieve dynamic callback arguments in JavaScript

How can I achieve dynamic callback arguments in JavaScript
I have a three functions I want to compose. Why am I doing this is because I want to encapsulate the details of the initDB so I can write less code. Here's what it looks like below:
const initDB = (schemas: any[]) =>
Realm.open({ path: 'CircleYY.realm', schema: schemas })
.then((realm: Realm) => ({ realm }))
.catch((error: Error) => ({ error }));
So basically this function just initialize a DB and it will return a DB instance or an Error.
I also have some specific database write functions like this below:
// Delete a message
const deleteOrder = (orderID: string, realm: Realm) => {
realm.write(() => {
const order = realm.objects('Orders').filtered(`primaryKey = ${id}`);
realm.delete(order);
});
};
and I have this three functions below:
makeDBTransaction(deleteOrder(id));
and
makeDBTransaction(writeCommentInOrder(orderId, comment))
and
const makeDBTransaction = async (callback: Function) => {
const { error, realm } = (await initDB([
OrderSchema,
ProductSchema,
])) as InitRealm;
if (error) return { error };
callback(realm); // Pass realm while maintaining the arguments specified in the callback which is dynamic
return realm.close();
};
I also want to pass the realm into the callback that can have more than 2 arguments.
How can I achieve that?
I think you can keep adding arguments in an array in a required order and then apply the arguments to the function and call that function.
//for example
function foo1(x, y) {
console.log(x,y);
}
function foo2(cb, y) {
const x = 3;
cb.apply(null, [x,y]);
}
foo2(foo1, 5);
//So your code will be like this
makeDBTransaction(deleteOrder, [id]);
const makeDBTransaction = async (callback: Function, arg: any[]) => {
const { error, realm } = (await initDB([
OrderSchema,
ProductSchema,
])) as InitRealm;
if (error) return { error };
arg.push(realm);
callback.apply(null, arg);
return realm.close();
};

Passing a callback function and creating an Observable from it

I cannot create a new Observable object out of the callback function, for example converting glob function to Observable stream:
const stream$ = fromCallback(() => glob('src/**/*.ts'));
const fromCallback = (cbWrapper) => {
const cb = cbWrapper();
const args = cb.arguments;
return Observable.create(observer => {
args.push((err, data) => {
if(err) {
observer.error(err);
} else {
observer.next(data);
}
observer.complete();
});
cb.call(null, args);
};
This is more or less what I want to do - create a fromCallback function which accepts my function in a wrapper, adds a new parameter which is a callback handler, and calls observer based on the results. But it doesn't work - the cbWrapper() returns always true for some reason.
What's wrong here? Is there a better solution to solve this?

How can I pass variable into an evaluate function?

I'm trying to pass a variable into a page.evaluate() function in Puppeteer, but when I use the following very simplified example, the variable evalVar is undefined.
I can't find any examples to build on, so I need help passing that variable into the page.evaluate() function so I can use it inside.
const puppeteer = require('puppeteer');
(async() => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
const evalVar = 'WHUT??';
try {
await page.goto('https://www.google.com.au');
await page.waitForSelector('#fbar');
const links = await page.evaluate((evalVar) => {
console.log('evalVar:', evalVar); // appears undefined
const urls = [];
hrefs = document.querySelectorAll('#fbar #fsl a');
hrefs.forEach(function(el) {
urls.push(el.href);
});
return urls;
})
console.log('links:', links);
} catch (err) {
console.log('ERR:', err.message);
} finally {
// browser.close();
}
})();
You have to pass the variable as an argument to the pageFunction like this:
const links = await page.evaluate((evalVar) => {
console.log(evalVar); // 2. should be defined now
…
}, evalVar); // 1. pass variable as an argument
You can pass in multiple variables by passing more arguments to page.evaluate():
await page.evaluate((a, b c) => { console.log(a, b, c) }, a, b, c)
The arguments must either be serializable as JSON or JSHandles of in-browser objects: https://pptr.dev/#?show=api-pageevaluatepagefunction-args
I encourage you to stick on this style, because it's more convenient and readable.
let name = 'jack';
let age = 33;
let location = 'Berlin/Germany';
await page.evaluate(({name, age, location}) => {
console.log(name);
console.log(age);
console.log(location);
},{name, age, location});
Single Variable:
You can pass one variable to page.evaluate() using the following syntax:
await page.evaluate(example => { /* ... */ }, example);
Note: You do not need to enclose the variable in (), unless you are going to be passing multiple variables.
Multiple Variables:
You can pass multiple variables to page.evaluate() using the following syntax:
await page.evaluate((example_1, example_2) => { /* ... */ }, example_1, example_2);
Note: Enclosing your variables within {} is not necessary.
It took me quite a while to figure out that console.log() in evaluate() can't show in node console.
Ref: https://github.com/GoogleChrome/puppeteer/issues/1944
everything that is run inside the page.evaluate function is done in the context of the browser page. The script is running in the browser not in node.js so if you log it will show in the browsers console which if you are running headless you will not see. You also can't set a node breakpoint inside the function.
Hope this can help.
For pass a function, there are two ways you can do it.
// 1. Defined in evaluationContext
await page.evaluate(() => {
window.yourFunc = function() {...};
});
const links = await page.evaluate(() => {
const func = window.yourFunc;
func();
});
// 2. Transform function to serializable(string). (Function can not be serialized)
const yourFunc = function() {...};
const obj = {
func: yourFunc.toString()
};
const otherObj = {
foo: 'bar'
};
const links = await page.evaluate((obj, aObj) => {
const funStr = obj.func;
const func = new Function(`return ${funStr}.apply(null, arguments)`)
func();
const foo = aObj.foo; // bar, for object
window.foo = foo;
debugger;
}, obj, otherObj);
You can add devtools: true to the launch options for test
I have a typescript example that could help someone new in typescript.
const hyperlinks: string [] = await page.evaluate((url: string, regex: RegExp, querySelect: string) => {
.........
}, url, regex, querySelect);
Slightly different version from #wolf answer above. Make code much more reusable between different context.
// util functions
export const pipe = (...fns) => initialVal => fns.reduce((acc, fn) => fn(acc), initialVal)
export const pluck = key => obj => obj[key] || null
export const map = fn => item => fn(item)
// these variables will be cast to string, look below at fn.toString()
const updatedAt = await page.evaluate(
([selector, util]) => {
let { pipe, map, pluck } = util
pipe = new Function(`return ${pipe}`)()
map = new Function(`return ${map}`)()
pluck = new Function(`return ${pluck}`)()
return pipe(
s => document.querySelector(s),
pluck('textContent'),
map(text => text.trim()),
map(date => Date.parse(date)),
map(timeStamp => Promise.resolve(timeStamp))
)(selector)
},
[
'#table-announcements tbody td:nth-child(2) .d-none',
{ pipe: pipe.toString(), map: map.toString(), pluck: pluck.toString() },
]
)
Also not that functions inside pipe cant used something like this
// incorrect, which is i don't know why
pipe(document.querySelector)
// should be
pipe(s => document.querySelector(s))

new object with async routines

I want to instantiate an object where the constructor performs async calls before returning. The purpose is to do async currying. I am using co. The below example fails. What am I doing wrong?
var co = require('co')
function asyncFunction() {
return new Promise(function (resolve) {
resolve()
})
}
function MyObject () {
co(function * () {
yield asyncFunction()
}).then(()=> {
this.runSomething = function() {
return 'something'
}
})
}
new MyObject().runSomething()
// TypeError: (intermediate value).runSomething is not a function
A new operator will always return an object immediately, and synchronously. You can't delay that operation.
You could instead create a function that will return a Promise for your object.
function makeObjectAsync() {
const asyncResult = asyncOperation();
return asyncResult.then(result => ({
runSomething() { /* ... */ }
}));
}
myObjectAsync()
.then(obj => {
// obj is ready to use.
return obj.runSomething();
});
You can combine that with co fairly easily to get read of some of the .then()s.

Categories

Resources