Testing a memoize async function - javascript

Need some help in writing test cases. I want to test whether getSomeData is called only once.
function getSomeData(foo, callback) {
return new Promise((resolve, reject) => {
setTimeout(()=> {
console.log('async request');
resolve(callback(2 * foo));
}, 1000);
});
}
Now I want to test whether memoized function is called once or twice even if memoizedGetSomeData is called twice
function memoize(fn) {
const cache = {};
return async function() {
const args = JSON.stringify(arguments);
console.log('arguments passed to memoize fn: ', args);
console.log('cache data: ', cache);
cache[args] = cache[args] || fn.apply(undefined, arguments);
return cache[args]
}
}
const memoizedGetSomeData = memoize(getSomeData);
memoizedGetSomeData(1, callback_fn);
memoizedGetSomeData(1, callback_fn);

Related

Javascript Promisification, why use "call"?

I want to understand why in the below example, the "call" method was used.
loadScript is a function that appends a script tag to a document, and has an optional callback function.
promisify returns a wrapper function that in turn returns a promise, effectively converting `loadScript' from a callback-based function to a promise based function.
function promisify(f) {
return function (...args) { // return a wrapper-function
return new Promise((resolve, reject) => {
function callback(err, result) { // our custom callback for f
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // append our custom callback to the end of f arguments
f.call(this, ...args); // call the original function
});
};
}
// usage:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);
loadScript():
function loadScript(src, callback) {
let script = document.createElement("script");
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
I understand that call is used to force a certain context during function call, but why not use just use f(...args) instead of f.call(this, ...args)?
promisify is a general-purpose function. Granted, you don't care about this in loadScript, but you would if you were using promisify on a method. So this works:
function promisify(f) {
return function (...args) { // return a wrapper-function
return new Promise((resolve, reject) => {
function callback(err, result) { // our custom callback for f
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // append our custom callback to the end of f arguments
f.call(this, ...args); // call the original function
});
};
}
class Example {
constructor(a) {
this.a = a;
}
method(b, callback) {
const result = this.a + b;
setTimeout(() => callback(null, result), 100);
}
}
(async () => {
try {
const e = new Example(40);
const promisifiedMethod = promisify(e.method);
const result = await promisifiedMethod.call(e, 2);
console.log(result);
} catch (error) {
console.error(error);
}
})();
That wouldn't work if promisify didn't use the this that the function it returns receives:
function promisifyNoCall(f) {
return function (...args) { // return a wrapper-function
return new Promise((resolve, reject) => {
function callback(err, result) { // our custom callback for f
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // append our custom callback to the end of f arguments
f(...args); // call the original function *** changed
});
};
}
class Example {
constructor(a) {
this.a = a;
}
method(b, callback) {
const result = this.a + b;
setTimeout(() => callback(null, result), 100);
}
}
(async () => {
try {
const e = new Example(40);
const promisifiedMethod = promisifyNoCall(e.method);
const result = await promisifiedMethod.call(e, 2);
console.log(result);
} catch (error) {
console.error(error);
}
})();

How to use the result of .then function to another function in js?

Please help I want to use the result of function 1 (Fn1) in function 2 (Fn2).
App={
st: null,//st is number value
Fn1: function() {
App.contracts.contractName.deployed().then(function(instance){
return instance.getST();
}).then(function(result){
App.st = result;
});
},
Fn2: function() {
alert(App.st)//
}
}
You need to call Fn1 before Fn2 to access it's value, so let's wrap Fn1 into Promise:
App = {
st: null,//st is number value
Fn1: function() {
return new Promise((resolve, reject) => {
App.contracts.contractName.deployed().then(function(instance){
return instance.getST();
}).then(function(result){
App.st = result;
resolve();
}).catch(function(err){
reject(err);
})
})
},
Fn2: function() {
alert(App.st)
}
}
or better with async/await:
App = {
st: null,//st is number value
Fn1: async function() {
try {
const instance = await App.contracts.contractName.deployed();
const result = await instance.getST();
App.st = result;
} catch(err) {
throw err;
}
},
Fn2: function() {
alert(App.st)
}
}
Now you can wait until Fn1 exec before calling Fn2:
App.Fn1().then(function() {
App.Fn2()
})
or using async/await:
await App.Fn1()
App.Fn2()
You can just return the Promise defined in Fn1
Fn1: function() {
return App.contracts.contractName.deployed().then((instance) => {
return instance.getST();
}).then((result) => {
// Note that we return the assignment
return App.st = result;
});
}
Then you have two options, either you can call Fn1 before Fn2
App.Fn1().then(st => App.Fn2());
Or you can adjust Fn2's implementation to call Fn1 first.
// simplistic solution
Fn2: function() {
App.Fn1().then(st => {
// do something with st
});
}
// more robust solution
Fn2: function() {
const resolveST = () => App.st != null ? Promise.resolve(App.st) : App.Fn1();
resolveST().then(st => {
// do something with st
})
}
Then using Fn2 is as simple as App.Fn2()

Resolve promise after callback it's executed

I have the following code on which I am trying to block the execution of the method _saveAddress multiple time, so I made a promise for this method.
const [pressEventDisabled, setPressEventDisabled] = useState(false);
<TouchableOpacity style={style.button_container} activeOpacity={1} disabled={pressEventDisabled} onPress={async () => {setPressEventDisabled(true); await _saveAddress(); setPressEventDisabled(false);}} >
The problem is that I want to resolve the promise after the callback method it's executed. It's there any way to wait for the dispatch function to execute or to resolve the promise inside the callback method?
This is the method for saving the address:
const _saveAddress = () => new Promise(async (resolve) => {
var valid = _validate();
if (valid) {
const address = createAddressJson();
if (addressId) {
var addressIdProperty = {
id: addressId
};
const newAddress = Object.assign(addressIdProperty, address);
dispatch(editAddress(newAddress, _onAddressSaveEditCallback));
} else {
dispatch(addAddress(address, _onAddressSaveEditCallback));
}
} else {
//notify
notifyMessage(strings.fill_required_inputs_validation);
resolve();
}
});
This is the callback method:
const _onAddressSaveEditCallback = async (success: boolean, apiValidations: any, address ? : Address, ) => {
if (success) {
if (typeof callback == 'function') {
callback(address);
}
await Navigation.pop(componentId);
} else {
setDataValidations(apiValidations);
}
};
Just do exactly what you say in the title. Nothing more, nothing less:
if (addressId) {
var addressIdProperty = {id: addressId};
const newAddress = Object.assign(addressIdProperty, address);
dispatch(editAddress(newAddress, async (s,v,a) => {
await _onAddressSaveEditCallback(s,v,a);
resolve();
}));
} else {
dispatch(addAddress(address, async (s,v,a) => {
await _onAddressSaveEditCallback(s,v,a);
resolve();
}));
}
Of course, since you are passing async () => {} to addAddress instead of _onAddressSaveEditCallback you have to call _onAddressSaveEditCallback yourself since addAddress will be calling the async () => ...
But mixing promises and callbacks like this isn't great. It leads to weird looking and sometimes confusing code. A better solution is to promisify addAddress:
function addAddressPromise (address) {
return new Promise((resolve, reject) => {
addAddress(address, (success, validations, address) {
if (success) return resolve(address);
else reject(validations)
});
});
}
Now you can wait for addAddress:
const _saveAddress = async () => {
// Don't create new promise here, we do it in addAddress..
// ...
let result = await addAddressPromise(address);
dispatch(result);
await _onAddressSaveEditCallback();
// ...
}

How to make a function return a promise if optional callback is not passed?

I could come up with
function squareAsync(val, callback) {
if (callback) {
setTimeout(() => {
if (Math.random() < 0.5) {
callback(undefined, val * val);
}
else {
callback(new Error('Failed!'));
}
}, 2000);
}
else {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
resolve(val * val);
}
else {
reject(new Error('Failed!'));
}
}, 2000);
});
}
}
I found another way for this
function squareAsync1(val, callback) {
let p = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
resolve(val * val);
}
else {
reject(new Error('Failed!'));
}
}, 2000);
});
if (callback) {
p.then(d => {
callback(undefined, d);
}, e => {
callback(e);
});
}
return p;
}
Which one of these is better or there is a more standard and elegant way of doing this? Can we do this using async/await?
You can do it like so:
function squareAsync(val, callback) {
const timeout = function(res, rej){
setTimeout(function(){
if (Math.random() < 0.5)
res(val*val);
else
rej(new Error('Failed!'));
}, 2000);
}
return typeof callback === 'function'
? timeout(callback.bind(null, undefined), callback)
: new Promise(timeout);
}
// CALLBACK EXAMPLE
squareAsync(5, (err, val) => {
if (err)
console.log(`Callback: ${err}`);
else
console.log(`Callback: ${val}`);
})
// PROMISE EXAMPLE
squareAsync(5)
.then(val => console.log(`Promise: ${val}`))
.catch(err => console.log(`Promise: ${err}`))
Explanation
Wrap your setTimeout call into one wrapper function timeout so that you don't have to repeat your almost identical code.
Let timeout function take two arguments: res and rej (resolve and reject)
Return timeout if callback is passed with a function, else return new Promise(timeout).
Now as to what happen in:
return typeof callback === 'function'
? timeout(callback.bind(null, undefined), callback)
: new Promise(timeout);
It translates to:
if (typeof callback === 'function'){
// Bind `null` as `this` value to `callback
// and `undefined` as its first argument (because no error).
// Need to to this because in `timeout` function,
// we call `res` with only 1 argument (computed value) if success.
const resolve = callback.bind(null, undefined);
// Don't need to bind anything
// because the first argument should be error.
const reject = callback;
// Call the function as if we are in a Promise
return timeout(resolve, reject);
}
// Use `timeout` function as normal promise callback.
return new Promise(timeout);
Hope you understand. Feel free to comment if confused.
More about bind.
async function squareAsync1(val, callback) {
let p = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
resolve(val * val);
}
else {
reject(new Error('Failed!'));
}
}, 2000);
});
if (callback) {
return p.then(d => {
return callback(undefined, d);
}, e => {
return callback(e);
});
}
return p;
}
Yes, your solution will work with async/await. Notice I just added return to the p.then
This way you can do something like:
const x = await squareAsync1(2, (e, v) => e ? 1 : v * 2)
And you will get x as either 1 (if the promise was rejected) or 8 (if the promise was successful)

Fluent async api with ES6 proxy javascript

So... I have some methods. Each method returns a promise.
myAsyncMethods: {
myNavigate () {
// Imagine this is returning a webdriverio promise
return new Promise(function(resolve){
setTimeout(resolve, 1000);
})
},
myClick () {
// Imagine this is returning a webdriverio promise
return new Promise(function(resolve){
setTimeout(resolve, 2000);
})
}
}
I'm trying to make end to end tests, so the prom chain must be linear (first click, next navigate, etc)
For now, I can do this...
makeItFluent(myAsyncMethods)
.myNavigate()
.myClick()
.then(() => myAsyncMethods.otherMethod())
.then(() => /*do other stuff*/ )
...with ES6 proxy feature:
function makeItFluent (actions) {
let prom = Promise.resolve();
const builder = new Proxy(actions, {
get (target, propKey) {
const origMethod = target[propKey];
return function continueBuilding (...args) {
// keep chaining promises
prom = prom.then(() => (typeof origMethod === 'function') && origMethod(...args));
// return an augmented promise with proxied object
return Object.assign(prom, builder);
};
}
});
return builder;
};
But, the thing I cannot do is the following:
makeItFluent(myAsyncMethods)
.myNavigate()
.myClick()
.then(() => myAsyncMethods.otherMethod())
.then(() => /*do other stuff*/ )
.myNavigate()
Because then is not a proxied method, and thus it does not return myAsyncMethods. I tried to proxy then but with no results.
Any idea?
thanks devs ;)
I would return wrapped Promises from yourAsyncMethods which allows mixing of sync and async methods with Proxy and Reflect and executing them in the correct order :
/* WRAP PROMISE */
let handlers;
const wrap = function (target) {
if (typeof target === 'object' && target && typeof target.then === 'function') {
// The target needs to be stored internally as a function, so that it can use
// the `apply` and `construct` handlers.
var targetFunc = function () { return target; };
targetFunc._promise_chain_cache = Object.create(null);
return new Proxy(targetFunc, handlers);
}
return target;
};
// original was written in TS > 2.5, you might need a polyfill :
if (typeof Reflect === 'undefined') {
require('harmony-reflect');
}
handlers = {
get: function (target, property) {
if (property === 'inspect') {
return function () { return '[chainable Promise]'; };
}
if (property === '_raw') {
return target();
}
if (typeof property === 'symbol') {
return target()[property];
}
// If the Promise itself has the property ('then', 'catch', etc.), return the
// property itself, bound to the target.
// However, wrap the result of calling this function.
// This allows wrappedPromise.then(something) to also be wrapped.
if (property in target()) {
const isFn = typeof target()[property] === 'function';
if (property !== 'constructor' && !property.startsWith('_') && isFn) {
return function () {
return wrap(target()[property].apply(target(), arguments));
};
}
return target()[property];
}
// If the property has a value in the cache, use that value.
if (Object.prototype.hasOwnProperty.call(target._promise_chain_cache, property)) {
return target._promise_chain_cache[property];
}
// If the Promise library allows synchronous inspection (bluebird, etc.),
// ensure that properties of resolved
// Promises are also resolved immediately.
const isValueFn = typeof target().value === 'function';
if (target().isFulfilled && target().isFulfilled() && isValueFn) {
return wrap(target().constructor.resolve(target().value()[property]));
}
// Otherwise, return a promise for that property.
// Store it in the cache so that subsequent references to that property
// will return the same promise.
target._promise_chain_cache[property] = wrap(target().then(function (result) {
if (result && (typeof result === 'object' || typeof result === 'function')) {
return wrap(result[property]);
}
const _p = `"${property}" of "${result}".`;
throw new TypeError(`Promise chain rejection: Cannot read property ${_p}`);
}));
return target._promise_chain_cache[property];
},
apply: function (target, thisArg, args) {
// If the wrapped Promise is called, return a Promise that calls the result
return wrap(target().constructor.all([target(), thisArg]).then(function (results) {
if (typeof results[0] === 'function') {
return wrap(Reflect.apply(results[0], results[1], args));
}
throw new TypeError(`Promise chain rejection: Attempted to call ${results[0]}` +
' which is not a function.');
}));
},
construct: function (target, args) {
return wrap(target().then(function (result) {
return wrap(Reflect.construct(result, args));
}));
}
};
// Make sure all other references to the proxied object refer to the promise itself,
// not the function wrapping it
Object.getOwnPropertyNames(Reflect).forEach(function (handler) {
handlers[handler] = handlers[handler] || function (target, arg1, arg2, arg3) {
return Reflect[handler](target(), arg1, arg2, arg3);
};
});
You would use it with your methods like
myAsyncMethods: {
myNavigate () {
// Imagine this is returning a webdriverio promise
var myPromise = new Promise(function(resolve){
setTimeout(resolve, 1000);
});
return wrap(myPromise)
},
// ...
Please note two things :
You might need a polyfill for Reflect : https://www.npmjs.com/package/harmony-reflect
We need to check proxy get handlers for built-in Symbols, e.g. : https://github.com/nodejs/node/issues/10731 (but also some browsers)
You can now mix it like
FOO.myNavigate().mySyncPropertyOrGetter.myClick().mySyncMethod().myNavigate() ...
https://michaelzanggl.com/articles/end-of-chain/
A promise is nothing more than a "thenable" (an object with a then() method), which conforms to the specs. And await is simply a wrapper around promises to provide cleaner, concise syntax.
class NiceClass {
promises = [];
doOne = () => {
this.promises.push(new Promise((resolve, reject) => {
this.one = 1;
resolve();
}));
return this;
}
doTwo = () => {
this.promises.push(new Promise((resolve, reject) => {
this.two = 2;
resolve();
}));
return this;
}
async then(resolve, reject) {
let results = await Promise.all(this.promises);
resolve(results);
}
build = () => {
return Promise.all(this.promises)
}
}
Them you can call it in both ways.
(async () => {
try {
let nice = new NiceClass();
let result = await nice
.doOne()
.doTwo();
console.log(nice);
let nice2 = new NiceClass();
let result2 = await nice2
.doOne()
.doTwo()
.build();
console.log(nice2, result2);
} catch(error) {
console.log('Promise error', error);
}
})();

Categories

Resources