Sequential async function explanation - javascript

This is a test case from one company. I need to translate this function into Typescript and explain how to test it. But I even don't understand what is going on here. I understand every part of that code, but a whole picture still doesn't clear for me. Could someone explain how that piece of code works step by step?
I think I need some simple analog for this. Maybe some metaphor. Also, I'm not sure how to test that to see how this function should work.
// Makes your async function sequential. It means that next call won't be performed until the previous one finishes.
// This function is a wrapper for async functions
export function sequentialize(func: (...args: any[]) => any) {
// we create an immideately resolved promise
// we can use that because of closure mechanism
let previousCall: Promise<any> = Promise.resolve();
// return wrapper that will call original function with args
return function (...args: any[]): Promise<any> {
// Here I'm a bit confused. I know that with then we set a callback that
// will work after promise will be fullfiled. But why we save this in variable
const currentCall: Promise<any> = previousCall.then(() =>
// call original wrapped async function with args
func.apply(this, args)
);
console.log(currentCall);
// this part is not clear for me too
previousCall = currentCall.catch(() => {});
return currentCall;
};
}
// I use this code to test
const url = "https://jsonplaceholder.typicode.com/todos/1";
const myAsyncFunction = (url) => {
return fetch(url)
.then((response) => response.json())
.then((json) => console.log(json));
};
const sequentializeFunc = sequentialize(myAsyncFunction);
sequentializeFunc(url);
UPDATE: add ts and some comments to parts I already understand.

Related

Jasmine - How to spy on a deep nested function

I have the following scenario:
file1.js:
async function fctionA(event) {
console.log('In fctionA()!');
let responseA = null;
const formattedEvent = formatEvent(event);
...
const b = await fctionB(formattedEvent);
responseA = someLogicUsing(b);
return responseA; // responseA will depend on 'b'
}
file2.js:
async function fctionB(formattedEvent) {
console.log('Now in fctionB()!');
let resB = null;
...
const c = await fctionC(formattedEvent);
...
resB = someLogicDependingOn(c); // resB will depend on 'c'
return resB;
}
async function fctionC(formattedEvent) {
console.log('AND now in fctionC()!');
let c = await someHttpRequest(formattedEvent);
...
return c;
}
Side notes:
Don't mind formatEvent(), someLogicUsing() orsomeLogicDependingOn() too much. Assume it's just sync logic using provided data)
formattedEvent would be anything depending on the original input event. Just to note functions will use it.
PROBLEM:
What i want to do is to unit test fctionA(), using Jasmine: I want to check the responseA after appying the logic on fctionB(), but mock the result of fctionC().
My (clearly) naive approach was:
file1.spec.js
import * as Handler from '../src/file1';
import * as utils from '..src//file2';
describe('fctionA', () => {
let response = null;
beforeAll(async () => {
const mockedEventA = { mockedInput: 'a' };
const mockedC = { mockedData: 1 };
const expectedResponse = { mockedResponse: 1234 };
spyOn(utils, 'fctionB').and.callThrough());
spyOn(utils, 'fctionC').and.returnValue(Promise.resolve(mockedC));
response = await Handler.fctionA(mockedEventA);
});
it('should return a proper response', () = {
expect(response).toEqual(expectedResponse);
});
});
But after checking logs, i can see that ´fctionC()´ does get executed (when as far as i understood, it shouldn't), therefore does not return the mocked result.
Then, after some try and error, i invoked fctionC() directly in fctionA() (instead of indirectly invoking it through´fctionB()´) just to see what happens, and I can spy it and return a mocked value. fctionC() does not execute (can't see log).
So, that makes me think that, at least the way I'm trying to spy on functions, only work for functions that are directly invoked by the function I'm calling, but not for nested ones-
I'm clearly not an expert in Jasmine, so I can't figure out another option. I looked a lot into docs and posts and blogs but nothing worked for me.
Is there a way to achieve what I try here? I guess I might be doing something really silly or not thinking it through.
Thanks!

Promise.resolve vs Promise.resolve().then()

A question asked here before, with the exact same title as this one, was answered with a "You should not use that, use this instead", I am looking to know what it does, not what else could I do, it's about understanding not a simple copy a paste.
My question is quite simple, what is the difference between these three approaches when creating a promise?
const API = (item, fail) =>
new Promise((resolve, reject) => {
if (fail) reject(item + ' ...with an error');
setTimeout(() => resolve(item), 1000);
});
(async () => {
const pro1 = Promise.resolve(API('I am inside resolve'));
const pro2 = Promise.resolve(API('I am inside resolve', true));
const pro3 = Promise.resolve().then(() => API('I am thenable'));
const pro4 = Promise.resolve().then(() => API('I am thenable', true));
const pro5 = new Promise((resolve) => resolve(API('I am a new promise')));
const pro6 = new Promise((resolve) => resolve(API('I am a new promise', true)));
const store = [pro1, pro2, pro3, pro4, pro5, pro6];
const results = await Promise.allSettled(store);
for (const { status, value, reason } of results) {
if (status === 'fulfilled') console.log(value)
else console.log(reason)
}
})();
The difference is in job to be done. While all of this methods are valid, they have different cost and predictability.
Promise.resolve() produces single resolved Promise instance, and depending on value provided to the call JS engine have information to optimize it. It makes all work to be done in a single call to underlying code of the JS engine (usually C++, but could be Java or WASM). So it's always the best choice.
Promise.resolve().then(() => API(/*...*/)) Produce several Promise instances: one at Promise.resolve() and other at .then() call. It also allocates more memory and make several (3 or more) redundant jumps between JS and the engine. It's hardly optimizable and requires intensive heuristics to be performed to figure out is this call optimizable. It's the worst option.
new Promise((resolve) => resolve(API(/* ... */)) allocates one function and one Promise instance and makes two jumps between JS and the engine. It's harder to optimize this call, due to nature of JS.
Promise.resolve().then()
In your examples, the then() makes no difference as you just resolve the promise, and get its data.
then() is typically used to chain promises, take this as an example:
Promise.resolve('foo')
// 1. Receive "foo", concatenate "bar" to it, and resolve that to the next then
.then(function(string) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
})
// 2. receive "foobar", register a callback function to work on that string
// and print it to the console, but not before returning the unworked on
// string to the next then
.then(function(string) {
setTimeout(function() {
string += 'baz';
console.log(string); // foobarbaz
}, 1)
return string;
})
Here we chain multiple promises then() off of a previously resolved promise, while maintaining the original data from the first resolve, though in this case we modify it with every new promise.
You can read more about promises and chaining here.

How to test a function composition with side effects without using mocks?

I have a function, MyComposedFunction, which is the function composition of 3 functions the second function, fn2, performs a POST request (a side effect) using the result of fn1 and passes this value to fn3.
const fn2 = async (fn1Result) => {
const result = await fetch(fn1Result.url, fn1Result.payload);
// some business logic
return fn2Results;
};
const MyComposedFunction = compose(fn3, fn2, fn1);
// My Test
expect(MyComposedFunction('hello world')).toBe('expected result');
I'd like to avoid writing unit tests for fn3, fn2, and fn1 and instead only test MyComposedFunction. My rationale is that it should not matter whether MyComposedFunction uses compose(...) or is one long giant function as long as MyComposedFunction works.
Is it possible to write a test for MyComposedFunction without having to mock fn2?
I would think that this must be a relatively common situation when trying to do functional programming in JavaScript but haven't been able to find a helpful resource so far.
You can dependency inject fetch into your function like so
const fn2Thunk = (fetcher = fetch) => async (fn1Result) => {
const result = await fetcher(fn1Result.url, fn1Result.payload);
// some business logic
return fn2Results;
};
const fetchMock = async () => ({ result: "blah" })
const MyComposedFunctionTest = compose(fn3, fn2Thunk(fetchMock), fn1);
// My Test
const result = await MyComposedFunctionTest('hello world')
expect(result).toBe('expected result');

Typescript - Function with multiple parameters as callback

I have the following code :
function builder<T extends Foo>(
getItems: (...) => Promise<T[]>, /* unsure what to put between parenthesis here */
) {
return async (...): Promise<Baz> => {
const items = await getItems(...); /* unsure what to put between parenthesis here */
// some manipulation here
return items;
}
My goal is to allow the builder function to accept an asynchronous callback. This callback can be called with various types of parameters. There could be one, or many parameters inside the callback.
For example, I could call the builder function such as :
// call 1
builder<SomeResult>((a, b) => someCallback(a, b));
// call 2
builder<AnotherResult>((c) => otherCallback(c));
I'm pretty new to typescript so not quite sure how to do this. I think maybe i should look into rest arguments but not exactly sure how to tackle the problem.
Any help would be much appreciated. Thanks a lot for the tips.
If anybody googles this, finally went with :
function builder<T extends Foo>(
getItems: (...addedParams: any[]) => Promise<T[]>,
) {
return async (...addedParams: any[]): Promise<Baz> => {
const items = await getItems(...addedParams);
// some manipulation here
return items;
}

Promise not waiting on one another

So my understanding of promises lead me to believe that my other promises would run one after another in my then chain but I'm doing something wrong here.
The code I'm using currently is as follows
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
...spinner code.... //this is omitted code
);
const getData = () => Promise.resolve(
someObj.getProducts('data.json')
);
const updateProduct = () => Promise.resolve(
setTimeout(()=>{
someObj.updateProductHTML()
}, 0)
);
const updateDom = () => {
setTimeout(()=>{
someObj.updateDOM()
}, 0)
};
and my promise chain
mainPromise()
.then(getData)
.then(updateProduct)
.then(updateDom)
;
They seem to be initially running in order but the Ajax call I have in getProducts also has a for loop worker to build my array of objects and is finishing after all my .thens run.
I am attempting to at least have my data call and worker finish before updateProduct and updateDOM runs
--- UPDATE ---
ok so with the revised promises set up as such as per suggestions in the comments and Samanime's answer
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
);
const getData = () => new Promise( resolve => {
console.log('getData');
someObj.getProducts('data.json');
resolve();
}
);
const updateProduct = () => new Promise( resolve =>{
console.log('updateProduct');
someObj.updateProductHTML();
resolve();
});
//execute promise chain
mainPromise()
.then(getData)
.then(updateProduct)
.then(page.updateDOM)
;
I updated the promises to not immediately resolve and am attempting to call resolve after I call my functions ( although I'm uneasy as to if resolve will be called before or after these functions ).
Unfortunately, I'm still getting the same behavior. I've added console logs to my functions as well as my promises and I'm getting this list back
log.spinner spinning
log.getData
log.updateProduct
log.A log from the function updateProduct calls
log.48 product objects created (for loop worker in my getProducts function)
log.Data retrieved and assigned
the last two logs would ideally be called after getData
None of the calls or functions outside of the ones provided are return promises, I'm working on legacy code and I'm moving away from the setTimeout trick as well as my results weren't consistent.
--UPDATE 2 --
The problem I'm having is known as Forking/Splitting. I just need to figure out chaining specifically to fix my issue.
-- FINAL --
this is what I ended up working out
// execute promise chain
mainPromise()
.then(getData);
//the timeout is a little hack to ensure the sequence is kept
mainPromise()
.then(() => {
setTimeout(() => {
myObj.updateProductHTML();
myObj.updateDOM();
}, 0);
});
apparently .then(foo).then(bar) just runs foo and bar at the same time
seems to be working ok right but I feel like somethings not right with it still.
I believe it's because Promise.resolve() doesn't do quite what you think it does.
Promise.resolve() creates a new Promise and resolves it immediately using the value of what it's given. Things like setTimeout return their id (an integer) immediately, so they aren't doing what you want. Your getProducts() is probably an async call, so it may be returning null or something as well (if it's returning a Promise or returns the value synchronously, then it's fine).
You're better off writing a normal Promise and calling resolve() at the appropriate time.
const mainPromise = () => Promise.resolve(
console.log('spinner spinning...')
...spinner code....
);
// Assuming it's already returning a Promise or synchronous response. If it isn't, then deal with it like the setTimeout ones below.
const getData = () => someObj.getProducts('data.json')
const updateProduct = () => new Promise(resolve => {
setTimeout(()=>{
someObj.updateProductHTML();
resolve();
}, 0)
});
// You don't NEED to in your example since it's at the end of the chain, but you probably want to wrap this too in case you add to the chain.
const updateDom = () => new Promise(resolve => {
setTimeout(()=>{
someObj.updateDOM();
resolve();
}, 0)
});

Categories

Resources