Passing arguments while running lodash flow asynchronously - javascript

Given the code below, how can I pass id to the applySaveAsync function?
var then = _.curry(function (f, thenable) {
return thenable.then(f);
});
var validateAsync = _.flow(
function () { return _(someCondition).showError(ERROR_01).value(); },
then(function () { return _(anotherCondition).showError(ERROR_02).value(); })
);
var save = _.flow(
validateAsync,
then(applySaveAsync),
then(saveCompleted)
);
function applySaveAsync(id) {
// Saving...
}
save(22); // Calling save function with some id.
I can get the id on the validateAsync function, but I cannot return it back since validateAsync should return a promise.
Any way to achieve that?

The simplest choice would be not to use _.flow for the definition of validateAsync.
Since validateAsync does not take parameters nor has a result, you should just change the definition of save to not use _.flow:
function save(id) {
return validateAsync()
.then(function(){ return applySaveAsync(id) })
.then(saveCompleted)
}
We could also change validateAsync to pass through the id:
function validateAsync(id) {
return _(someCondition).showError(ERROR_01).value()
.then(function () { return _(anotherCondition).showError(ERROR_02).value(); })
.then(_.constant(id));
}
and even do that while still using _.flow
var validateAsync = _.flow(
function(id) { return _(someCondition).showError(ERROR_01).value().then(_.constant(id)); },
then(function(id) { return _(anotherCondition).showError(ERROR_02).value().then(_.constant(id)); })
);
but I would advise against that since validateAsync is not supposed to be a function that does takes parameters.
Let's write a wrapper function for such instead to let us do the pass-around in a functional way:
function pass(fn) {
return function(id) {
return fn().then(function() {
return id;
});
}
}
(if you prefer, you can try to compose that from then, _.constant and more)
so that one can write
var save = _.flow(
wrap(validateAsync),
then(applySaveAsync),
then(saveCompleted)
);

I found this package useful for you. In Async cases, you can use this package.
Although flow is one of the best implementations for declarative programming, it doesn't support modern JS programming style.
import { Conductor } from '#puzzleio/conductor';
const conductor = Conductor.createDefault();
const myAsyncWorkflow = conductor
.add(validateAsync)
.if({
check: item => item.isValid === true,
handler: item => console.log('Item is valid')
},
{
// else block
handler: item => console.log('Validation failed')
});
myAsyncWorkflow.run(obj)
.then(() => console.log('Successfully validated'))
.catch(console.error);

Related

Exported function to pass arguments and a constant to another function

I don't really know how to describe this, but I'll try explain it.
I want to be able to call func1() and func2(), but going through handler() in a module.
I want it in a way where calling module.exported1("foo") will call handler(func1, "foo"), in turn calling func1("foo"). The issue I'm having is that if I export 'exported1' as handler(func1), I can't pass any arguments exported1 was called with (As far as I know). Is there a workaround for this?
NOTE: It is a module, and I need it to be exported without the user needing to provide func1 and func2 to handler().
function func1(args) {
...
}
function func2(args) {
...
}
function handler(func, args) {
return func()
}
module.exports = {
exported1 = handler(func1, ...),
exported2 = handler(func2, ...)
}
Not sure I get why to use this pattern, but I am sure there is more to the code and guess you could do the following:
function func1(args) {
console.info(`func1 ${args}`);
}
function func2(args) {
console.info(`func2 ${args}`);
}
function handler(func, args) {
return func(args);
}
module.exports = {
exported1: (args) => {
return handler(func1, (args));
},
exported2: (args) => {
return handler(func2, (args));
},
};
You just need to export the function:
module.exports = {
exported = handler
}
Or, just:
exports.exported = handler
Now, after import, you can call with parameters:
exported(func1,...)
exported(func2,...)
After reading your edited question, I think you want to do something like this but I'm not pretty sure:
function handler(func) {
// you can replace it with function(args) { instead of arrow function
return (args) => {
return func(args)
}
}
module.exports = {
exported1 = handler(func1),
exported2 = handler(func2)
}
exported1(args)

How to unit test nested subscribe methods in Angular?

MethodToBeTested() {
this.serviceA.methodA1().subscribe((response) => {
if (response.Success) {
this.serviceA.methodA2().subscribe((res) => {
this.serviceB.methodB1();
})
}
});
}
Here is the scenario.
Things to test:
serviceA.methodA1(). was called.
if response.Success then check if serviceA.methodA2() was called
check if serviceB.methodB1() was called when serviceA.methodA2() received value.
first, one is easy to test.
let spy = spyOn(serviceA, 'methodA1');
expect(spy).toHaveBeenCalled();
But does one test 2 and 3?
let spy= spyOn(serviceA, 'methodA1').and.returnValue({subscribe: () => {success:true}});
subject.MethodToBeTested();
something like that?
Alright, so I figured out what I am looking for is callFake
it('should test inside of subscribe', () => {
let spy = spyOn(serviceA, 'methodA1').and.callFake(() => {
return of({ success: true });
});
let spy2 = spyOn(serviceA, 'methodA2').and.callFake(() => {
return of({ success: true });
});
let spy3 = spyOn(serviceB, 'methodB1').and.returnValue(of({ success: true }));
subject.MethodToBeTested();
expect(spy3).toHaveBeenCalled();
});
I learned that returnValue won't actually execute the inside of the subscribe while callFake will with the data you provide inside it.
It would be better to not use a nested subscribe.
Something like this could be a sollution:
let $obs1 = this.serviceA.methodA1().pipe(share());
let $obs2 = $obs1.pipe(switchMap(x => this.serviceA.methodA2()));
$obs1.subsribe(logic1 here...);
$obs2.subsribe(logic2 here...);

Load AJAX collections in order in JavaScript

How to ensure, in JavaScript (jquery) that some actions are performed one after other, in an order.
Say, I need to load schools collection BEFORE loading teachers, in order to assing the myTeacher.SchoolName = schools[myTeacher.SchoolId].name;
The pseudo code bellow:
const studentsUrl='api/students', teachersUrl='api/teachers', schoolsUrl='api/schools';
let students = null, teachers = null, schools = null;
$(document).ready(function () {
getSchools();
getTeachers();
getStudents();
});
function getSchools() {
$.get(schoolsUrl, function (data) {
window.schools = data;
});
}
function getTeachers() {
$.get(teachersUrl, function (data) {
window.teachers = data;
// >>> SHOULD BE SURE, SCHOOLS already loaded!!!
$.each(teachers, function (key, item) {
item.school = schools[item.schoolId].name;
});
});
}
function getStudents() {
$.get(studentsUrl, function (data) {
window.students = data;
// >>> SHOULD BE SURE, TEACEHRS already loaded!!!
$.each(students, function (key, item) {
item.teacher = teachers[item.teacherId].name;
});
});
}
PS.
Is there another way to assure order but the encapsulation of one function at the end of another?
As others already suggested you can chain requests.
I made few changes to your code.
Added Strict Mode it helps to prevent bugs
The code wrapped in IFFE in order to prevent global pollution
If all apis belong to the same server you can process all this data on server side
and return one filled json.
in this way your server will do a little extra work on constructing this json but in other hand you will make only one ajax request instead of 3.
This will work faster and you can cache this json for some time
Code for the first solution
(function () {
'use strict';
const studentsUrl = 'api/students';
const teachersUrl = 'api/teachers';
const schoolsUrl = 'api/schools';
let students = null;
let teachers = null;
let schools = null;
var scoolData = {
schools: null,
teachers: null,
students: null
};
$(document).ready(function () {
getSchools().then(function (schools) {
scoolData.schools = schools;
getTeachers().then(function (teachers) {
scoolData.teachers = teachers;
$.each(scoolData.teachers, function (key, item) {
item.school = scoolData.schools[item.schoolId].name;
});
});
});
});
function getSchools() {
return $.get(schoolsUrl);
}
function getTeachers() {
return $.get(teachersUrl,
function (result) {
scoolData.teachers = result;
// >>> SHOULD BE SURE, SCHOOLS already loaded!!!
$.each(teachers, function (key, item) {
item.school = scoolData.schools[item.schoolId].name;
});
});
}
})();
Since you only need all the results available and each request does not depend on the previous you can use jQuery.when
let students = null;
let teachers = null;
let schools = null;
$(document).ready(function() {
$.when(
getSchools(),
getTeachers()
).done(function(shoolResults, teacherResults) {
window.schools = shoolResults;
window.teachers = teacherResults;
handleTeachers();
getStudents();
});
function getSchools() {
return $.ajax({
type: 'GET',
url: schoolsUrl
});
}
function getTeachers() {
return $.ajax({
type: 'GET',
url: teachersUrl
});
}
function handleTeachers() {
$.each(teachers, function (key, item) {
item.school = schools[item.schoolId].name;
});
}
});
If you want them in order (though I'm not sure I understand why, since you retrieve all schools/teachers/students anyway), you can simply do this.
Note: get* functions are dummies in the following sample. Instead, just return the result of $.get calls from them:
function getSchools() {
return Promise.resolve({1: {name: 'school1'}});
}
function getTeachers() {
return Promise.resolve({1: {name: 'teacher1', schoolId: 1}});
}
function getStudents() {
return Promise.resolve({1: {name: 'student1', teacherId: 1}});
}
(async () => {
const schools = await getSchools();
const teachers = await getTeachers();
const students = await getStudents();
// Alternative for the $.each code
Object.values(teachers).forEach(teacher => teacher.school = schools[teacher.schoolId].name);
Object.values(students).forEach(student => student.teacher = teachers[student.teacherId].name);
console.log(schools, teachers, students);
})();
Another note: this is ES8 code, I'll post a non async/await version if you need to support older browsers and can't use a transpiler like Babel.
Non ES8-dependent code:
function getSchools() {
return Promise.resolve({1: {name: 'school1'}});
}
function getTeachers() {
return Promise.resolve({1: {name: 'teacher1', schoolId: 1}});
}
function getStudents() {
return Promise.resolve({1: {name: 'student1', teacherId: 1}});
}
let schools = null, teachers = null, students = null;
getSchools().then(_schools => {
schools = _schools;
return getTeachers();
}).then(_teachers => {
teachers = _teachers;
return getStudents();
}).then(_students => {
students = _students;
for (var _ in teachers) {
teachers[_].school = schools[teachers[_].schoolId].name;
}
for (var _ in students) {
students[_].teacher = teachers[students[_].teacherId].name
}
console.log(schools, teachers, students);
});
Call getTeachers(); when getSchools(); return success or complete, success preferred since complete runs if there's an error..
I think you are looking for this one.
getSchools().done(function(data){
var someId = data.findThatId;
getTeachers(someId);
});
You will need to return data from ajax call to get data in done.
You may load them asynchronously but you have to wait until both calls are finished.
To achieve this, add return before your ajax calls and combine the results in your ready function (not in the success handler of the teachers call):
let schoolsPromise = getSchools();
let teachersPromise = getTeachers();
$.when(schoolsPromise, teachersPromise)
.then((schools, teachers) => {
$.each(teachers, (key, item) => {
item.school = schools[item.schoolId].name;
});
});

Promises in Ionic 2 / Angular 2, how to?

I have two functions:
this.geQuizStorage();
this.getQuizData();
geQuizStorage() {
this.quizStorage.getAnswers().then(data => {
return data;
});
}
getQuizData() {
this.quizData.getQuiz().then(data => {
return data;
});
}
I am trying use promises for the 2 functions and wait until both are done, something like:
http.when(this.geQuizStorage(), this.getQuizData()).when(data => {
// data[0] first function response
// data[1]
})
any ideas how to do this in Ionic 2 / Angular 2
You can do this with ES6 promise's all function. No need for external libraries.
Promise.all([this.geQuizStorage(), this.getQuizData()]).then(data => {
//do stuff with data[0], data[1]
});
Your functions should return promises in order for this to work, so I suggest the following modification:
geQuizStorage() {
return this.quizStorage.getAnswers().then(data => {
return data;
});
}
getQuizData() {
return this.quizData.getQuiz().then(data => {
return data;
});
}
Basically you don't need to create another wrapper function for your service call, just to return a data(unless you have your validation logic out there to validate data). Then pass those two function in Observable.forkJoin by passing method promises/observable's & subscribe over that observable to wait till those get complete.
Observable.forkJoin([this.getQuizData(),this.geQuizStorage()])
.subscribe(data => {
console.log(data[0], data[1]);
//both call succeeded
});

Unsubscribe from Redux store when condition is true?

I'm employing the suggestion from #gaearon to setup a listener on my redux store. I'm using this format:
function observeStore(store, select, onChange) {
let currentState;
if (!Function.prototype.isPrototypeOf(select)) {
select = (state) => state;
}
function handleChange() {
let nextState = select(store.getState());
if (nextState !== currentState) {
currentState = nextState;
onChange(currentState);
}
}
let unsubscribe = store.subscribe(handleChange);
handleChange();
return unsubscribe;
}
I'm using this in an onEnter handler for a react-router route:
Entity.onEnter = function makeFetchEntity(store) {
return function fetchEntity(nextState, replace, callback) {
const disposeRouteHandler = observeStore(store, null, (state) => {
const conditions = [
isLoaded(state.thing1),
isLoaded(state.thing2),
isLoaded(state.thing3),
];
if (conditions.every((test) => !!test) {
callback(); // allow react-router to complete routing
// I'm done: how do I dispose the store subscription???
}
});
store.dispatch(
entities.getOrCreate({
entitiesState: store.getState().entities,
nextState,
})
);
};
};
Basically this helps gate the progression of the router while actions are finishing dispatching (async).
My problem is that I can't figure out where to call disposeRouteHandler(). If I call it right after the definition, my onChange function never gets a chance to do it's thing, and I can't put it inside the onChange function because it's not defined yet.
Appears to me to be a chicken-egg problem. Would really appreciate any help/guidance/insight.
How about:
Entity.onEnter = function makeFetchEntity(store) {
return function fetchEntity(nextState, replace, callback) {
let shouldDispose = false;
const disposeRouteHandler = observeStore(store, null, (state) => {
const conditions = [
isLoaded(state.thing1),
isLoaded(state.thing2),
isLoaded(state.thing3),
];
if (conditions.every((test) => !!test) {
callback(); // allow react-router to complete routing
if (disposeRouteHandler) {
disposeRouteHandler();
} else {
shouldDispose = true;
}
}
});
if (shouldDispose) {
disposeRouteHandler();
}
store.dispatch(
entities.getOrCreate({
entitiesState: store.getState().entities,
nextState,
})
);
};
};
Even though using the observable pattern leads to some buy-in, you can work around any difficulties with normal js code. Alternatively you can modify your observable to suit your needs better.
For instance:
function observeStore(store, select, onChange) {
let currentState, unsubscribe;
if (!Function.prototype.isPrototypeOf(select)) {
select = (state) => state;
}
function handleChange() {
let nextState = select(store.getState());
if (nextState !== currentState) {
currentState = nextState;
onChange(currentState, unsubscribe);
}
}
unsubscribe = store.subscribe(handleChange);
handleChange();
return unsubscribe;
}
and
Entity.onEnter = function makeFetchEntity(store) {
return function fetchEntity(nextState, replace, callback) {
const disposeRouteHandler = observeStore(store, null, (state, disposeRouteHandler) => {
const conditions = [
isLoaded(state.thing1),
isLoaded(state.thing2),
isLoaded(state.thing3),
];
if (conditions.every((test) => !!test) {
callback(); // allow react-router to complete routing
disposeRouteHandler();
}
}
store.dispatch(
entities.getOrCreate({
entitiesState: store.getState().entities,
nextState,
})
);
};
};
It does add a strange argument to onChange but it's just one of many ways to do it.
The core problem is that handleChange gets called synchronously immediately when nothing has changed yet and asynchronously later. It's known as Zalgo.
Inspired by the suggestion from #DDS, I came up with the following alteration to the other pattern mentioned in #gaearon's comment:
export function toObservable(store) {
return {
subscribe({ onNext }) {
let dispose = this.dispose = store.subscribe(() => {
onNext.bind(this)(store.getState())
});
onNext.bind(this)(store.getState());
return { dispose };
},
dispose: function() {},
}
}
This allows me to invoke like:
Entity.onEnter = function makeFetchEntity(store) {
return function fetchEntity(nextState, replace, callback) {
toObservable(store).subscribe({
onNext: function onNext(state) {
const conditions = [/* many conditions */];
if (conditions.every((test) => !!test) {
callback(); // allow react-router to complete routing
this.dispose(); // remove the store subscription
}
},
});
store.dispatch(/* action */);
};
};
The key difference is that I'm passing a regular function in for onNext so as not to interfere with my bind(this) in toObservable; I couldn't figure out how to force the binding to use the context I wanted.
This solution avoids
add[ing] a strange argument to onChange
... and in my opinion also conveys a bit more intent: this.dispose() is called from within onNext, so it kinda reads like onNext.dispose(), which is exactly what I want to do.

Categories