How to write a function with an Observable? - javascript

I want to create a function that returns an Observable.The description of the function is as follows:
The function should be delayedAlert(message:string, time:number) that returns an Observable.
The function should contain setTimeout function inside delayedAlert which prints message after the set 'time' value.
Ex:
delayedAlert(message, time){
return new Observable//how to implement setTimeout function here?

Use Observable.create to create the observable, and in the first callback, write the logic to populate the observable, which in your case is the setTimeout.
function delayedAlert(msg, time) {
return Observable.create(
observer => setTimeout(() => observer.onNext(msg), time));
}
Then to use it:
delayedAlert("Hi, Sally", 1000).subscribe(msg => alert(msg));
However, if you are using observables, you don't need to use setTimeout; use delay instead, applied to of, which creates an observable from individual value(s):
function delayedAlert(msg, time) {
return Observable.of(msg).delay(time);
}
Since it's so easy to write it this way, you probably don't need the delayedAlert function at all:
const observable = Observable.of("Hi, Sally").delay(1000);
observable.subscribe(msg => alert(msg));

Related

Have a question about react hook usage for "useEffect"

// ....react component
const [fruitDetail, setFruitDetail] = useState(null);
useEffect(() => {
if (!fruitName) {
return;
}
// Method 1
getFruit(fruitName).then((data) => {
console.log(data);
setFruitDetail(data);
});
// Method 2
getFruit(fruitName).then(setFruitDetail);
}, [fruitName]);
return fruitDetail;
I'm very curious about that why Method 1 and Method 2 are equivalent. Is it a Syntactic sugar ?
You can look at method 1 as an explicit way of calling the function. The .then method gets passed the result of getFruit(fruitName)..which you could name whatever you wanted, in your case you chose to call it data. Then you used that variable, data to make two function calls. So you explicitly refer the response by the variable name data.
In your method 2, the variable is implicit. Meaning, because of the way .then works, Javascript knows .then expects one argument, which is the result of the getFruit(fruitName). The .then method takes the result and passes it as the first argument to setFruitDetail...it would also be the same thing to say getFruit(fruitName).then(response => setFruitDetail(response)).You just don't need to specifically put the response variable since it is the only thing being passed and only thing being used.
then function calls any passed function/callback with the promise result.
setFruitDetail is just a function so is ()=>{} albeit anonymous one. You can also pass console.log and see what it does like this getFruit(fruitName).then(console.log). I would suggest to have a look at basic promise implementation.
Is it a Syntactic sugar ?
No, it is not. Because, it's just the another way to pass callback function.
There is example to make clear:
let api = async () => {
return new Promise((solve, reject) => {
setTimeout(() => solve('result'), 1000);
})
};
//your setFruit is a callback. Let example:
let setFruitDetail = (fruit) => console.log(fruit);
//there is no different when you pass a call back into then function.
api().then(setFruitDetail);
api().then(fruit => setFruitDetail(fruit));
//you can imagine that in then function will get param you input into to call a callback.
let otherApi = (callback) => {
let result = "result";
callback("result");
}
otherApi(setFruitDetail);
otherApi(fruit => setFruitDetail(fruit));

Return result of .then() lambda expression as function result

I'm relatively new to js so please forgive me if my wording isn't quite right. I've also created a jsfiddle to demonstrate the issue.
Overview
In the app I'm working on, I have a function with a jquery ajax call, like this:
function scenario1(ajaxCfg) {
return $.ajax(ajaxCfg)
}
I want to change this function, but without in any way changing the inputs or outputs (as this function is called hundreds of times in my application).
The change is to make a different ajax call, THEN make the call specified. I currently have it written like this:
function callDependency() { //example dependency
return $.ajax(depUri)
}
function scenario2(ajaxCfg) {
return callDependency().then(() => $.ajax(ajaxCfg))
}
Desired Result
I want these two returned objects to be identical:
let result1 = scenario1(exampleCall)
let result2 = scenario2(exampleCall)
More specifically, I want result2 to return the same type of object as result1.
Actual Result
result1 is (obviously) the result of the ajax call, which is a jqXHR object that implements the promise interface and resolves to the same value as result2, which is a standard promise.
Since result2 is not a jqXHR object, result2.error() is undefined, while result1.error() is defined.
I did attempt to mock up these methods (simply adding a .error function to the return result, for example), but unfortunately even when doing this, result1.done().error is defined while result2.done().error is undefined.
Wrapping (or unwrapping) it up
In a nutshell, I want to return the jqXHR result of the .then() lambda function in scenario2 as the result of the scenario2 function. In pseudocode, I want:
function scenario2(ajaxCfg) {
return callDependency().then(() => $.ajax(ajaxCfg)).unwrapThen()
} //return jqXHR
What about something like this? The approach is a little different, but in the end you can chain .done() etc. to the scenario2() function:
const exampleCall = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
const depUri = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
function callDependency() { //example dependency
return $.ajax(depUri).done(() => console.log('returned callDependancy'))
}
let obj = { //creating an object with the scenario2 as a method so that I can bind it with defer.promise()
scenario2: function(ajaxCfg) {
return $.ajax(ajaxCfg).done(() => console.log('returned senario2')) // Purposely NOT calling the exampleCall() function yet
}
}
defer = $.Deferred(); // Using some JQuery magic to be able to return a jqXHR
defer.promise(obj); // Set the object as a promise
defer.resolve(callDependency()); // Invoking the callDependency() by default on promise resolve
obj.done(() => {
obj.scenario2() // Resolving so the callDependency() function can be called
}).scenario2(exampleCall).done(() => { // Here you can invoke scenario2 and FINALLY chain whatever you want after everything has been called
console.log('Here I can chain whatever I want with .done\(\) or .fail\(\) etc.')
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
What I think is cool about this way of doing it is that you can just keep adding methods to the object that you created, and then all your secondary functions that are built on top of callDependency() can be in one place. Not only that, but you can reuse those same methods on top of other AJAX calls.
Read more about this here.
I hope this helps!
I feel like your life would be made a lot easier if you used async/await syntax. Just remember though that async functions return a promise. So you could instead write:
async function scenario2(ajaxCfg) {
let jqXhrResult;
try {
await callDependency();
jqXhrResult = {
jqXhr: $.ajax(ajaxCfg)
};
} catch() {
// Error handling goes here
}
return jqXhrResult;
}
I actually thought of a way easier way to do this.
You can do it by adding a method to the function constructor's prototype object. That way any created function can inherit that method and you can still use the .done() syntax. It's referred to as prototypal inheritance:
const exampleCall = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
const depUri = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
function callDependency() {
return $.ajax(depUri).done(() => console.log('returned callDependancy'))
}
Function.prototype.scenario2 = function(ajaxCfg, ...args) {
return this(...args).then(() => $.ajax(ajaxCfg))
}
callDependency.scenario2(exampleCall).done(data => {
console.log(data)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Debounce a function execution in a watcher (AngularJS)

I have a watch on vm.search variable, which is a ng-model of an input element. When I type something, I want to save it to database with a delayedSave function, but I don't want to save everything user types. So i decided to use lodash _.debounce function, but the problem is that this function doesn't work correclty within a $scope.$watch. delayedSave executes as many times as $scope.$watch function.
$scope.$watch('vm.search', nv => {
let savedQuery = _.find(vm.searchQueries, {query: nv});
if (savedQuery) {
vm.currentSearchQuery = savedQuery;
}
let runDebounce = _.debounce(delayedSave, 1000);
runDebounce(nv);
});
I could set debounce to $watch's callback, but I need to execute the code, I've written below, every time vm.seach has changed.
let savedQuery = _.find(vm.searchQueries, {query: nv});
if (savedQuery) {
vm.currentSearchQuery = savedQuery;
}
The same runDebounce method should be called repeatedly for the debounce to work. Since you are recreating the runDebounce function on each digest cycle, you're running a different method each time. Since the method is not called again, the debounce timeout passes, and the wrapped method delayedSaved is invoked.
Move the creation of the debounced function runDebounce out of the $watch callback:
const runDebounce = _.debounce(delayedSave, 1000);
$scope.$watch('vm.search', nv => {
const savedQuery = _.find(vm.searchQueries, {query: nv});
if (savedQuery) {
vm.currentSearchQuery = savedQuery;
}
runDebounce(nv);
});

What does .push.bind do?

I'm trying to learn coding in Javascript and came across this question and have a few questions. What does results.push.bind(results) do in this question? Please see question below:
Suppose getData is a function that takes a query object and returns a promise for the result of the query. Suppose also that someArrayOfQueries is an array of query objects. Explain what would be printed by the following code and why:
function runMultipleQueries(queries) {
var results = [];
queries.forEach(doQuery);
return results;
function doQuery(query) {
getData(query)
.then(results.push.bind(results));
}
}
function log(value) {
console.log(value);
}
runMultipleQueries(someArrayOfQueries).forEach(log);
In Javascript, unlike in other Object Oriented languages, the variable this is quite complicated and can be changed many times.
When working with arrays, the function push takes the array it is called "on", and puts a new element to the end of it. In this case, the push function knows what array it is working on by reading the this variable.
Imagine this example code:
var myDataStructure = {};
myDataStructure.value = 0;
function addOneToValue() {
this.value += 1;
}
myDataStructure.addOne = addOneToValue;
myDataStructure.addOne(); // here - the variable `this` == myDataStructure
However, if you call the function addOneToValue with the code addOneToValue(), the value of this is not in fact myDataStructure. It is undefined, or a global object like window.*
To manually force addOneToValue to always use myDataStructure as the this value, you can do the following:
addOneToValue = addOneToValue.bind(myDataStructure);
Now you can safely call addOneToValue(), since the this value was bound to myDataStructure.
In your code, runMultipleQuery is passing the function to another handler, who will not call the function like results.push. That means when the function is called, push will again have an uncertain this value. However, by calling bind, we can ensure that runMultipleQuery calls the push function and forces this to be the results variable.
What results.push.bind(results) does is it forces the method call of push to use the scope of results.
TLDR;
results is an Array. results.push() is a call to Array#push, but by passing the function results.push directly to the Promise Promise#then it will be called within a different scope.
That is, the keyword this will reference a different object.
By using bind (e.g. - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind), the scope will be "bound" to a specific Object.
The reason for this is because JavaScript is a prototype language, rather than an object-oriented language.
What does .push.bind do?
Returns the .length of the array, when called
function runMultipleQueries(queries) {
var results = [];
return Promise.all(queries.map(doQuery));
function doQuery(query) {
return Promise.resolve(query)
.then(results.push.bind(results))
}
}
function log(value) {
console.log(value);
}
runMultipleQueries([10, 20, 30]).then(log);
Note, that as astutely observed and alerted to by #Bergi, the pattern .then(results.push.bind(results)) actually returns the .length of the array.
One solution which still includes the use of the pattern is to chain a second .then() to get the .length returned by previous .then() and return the element of the array by subtracting 1 from the value and using bracket notation
function runMultipleQueries(queries) {
var results = [];
return Promise.all(queries.map(doQuery));
function doQuery(query) {
return Promise.resolve(query)
.then(results.push.bind(results))
.then(len => results[len - 1]);
}
}
function log(value) {
console.log(value);
}
runMultipleQueries([10, 20, 30]).then(log);
though creating results array is not necessary when using Promise.all(), which returns an array of values
.forEach() alone will not await the previous result of getData() call, results will probably be an empty array at runMultipleQueries(someArrayOfQueries).forEach(log);
Use Promise.all() and .map() instead of .forEach(), return the Promise from getData() call
function runMultipleQueries(queries) {
return Promise.all(queries.map(doQuery));
function doQuery(query) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(query)
}, Math.floor(Math.random() * 1000))
})
}
}
function log(value) {
console.log(value);
}
runMultipleQueries([1,2,3]).then(log);

How to create promises from library

I am confused on this one because every tutorial I have found so far assumes I can edit the library code, or that the library only has call backs or the call back as the last parameter.
The library I am using has every function set up like
function(successCallBack(result), FailCallBack(error),options)
So in each instance, I end up using code like
var options={stuff1:1, stuff2:2};
doStuff(success,failure,options);
function success(result){
//handle, usually with another call to a similar function, chaining callbacks together
};
function failure(error){
//handle error
};
How do I convert these into promises when I only have control of the call, and the success and failures?
Also, as a bonus, the chains are accessing variables outside them.
var options={stuff1:1, stuff2:2};
doStuff(success,failure,options);
function success(result){
var options2={stuff1:1, stuff2:2};
doStuff2(function(result2){
processStuff(result1, result2);
},function(error){
//handle error
},options2)
};
function failure(error){
//handle error
};
function processSuff(result1,result2){
//do things to results
}
Thanks
You could use the following function. It accepts a function to promisify and options, and returns promise:
let promisify = (fn, opts) => {
return new Promise((resolve, reject) => {
fn(resolve, reject, opts);
});
}
It could be used as follows:
promisify(doStuff, options)
.then(data => console.log('Success call', data))
.catch(err => console.log('Error', err));
Rather than call promisify every time you want to use your function, you can wrap it once and get a new function that you can just use as needed. The new function will return a promise and resolve/reject instead of the success and fail callbacks. This particular version of promisify is specifically coded for the calling convention you show of fn(successCallback, errorCallback, options). If you have other functions with different calling conventions, then you can create a different promisify version for them:
// return a promisified function replacement for this specific
// calling convention: fn(successCallback, errorCallback, options)
function promisify1(fn) {
return function(options) {
return new Promise((resolve, reject) => {
fn(resolve, reject, options);
});
}
}
So, then you would use this like this:
// do this once, somewhere near where doStuff is imported
let doStuffPromise = promisify1(doStuff);
// then, anytime you would normally use doStuff(), you can just use
// doStuffPromise() instead
doStuffPromise(options).then(results => {
// process results here
}).catch(err => {
// handle error here
});
The advantage of doing it this way is that you can "promisify" a given function of set of functions just once in your project and then just use the new promisified versions.
Suppose you imported an object that had a whole bunch of these types of interfaces on it. You could then make yourself a promisifyObj() function that would make a promisified interface for all the functions on the object.
// a "Promise" version of every method on this object
function promisifyAll(obj) {
let props = Object.keys(obj);
props.forEach(propName => {
// only do this for properties that are functions
let fn = obj[propName];
if (typeof fn === "function") {
obj[propName + "Promise"] = promisify1(fn);
}
});
}
So, if you had an object named myModule that had all these methods with this non-promise interface on it, you could promisify that module in one step:
promisifyAll(myModule);
And, then you could use any method from that object by just adding the "Promise" suffix to the method name:
myModule.someMethodPromise(options).then(results => {
// process results here
}).catch(err => {
// handle error here
});

Categories

Resources