How does JavaScript know to execute the fail function? - javascript

I'm working with jasmine and Supertest on an existing project.
let checkResult = require('./check-result');
it('should do something', function(done){
request
.post('/route')
.expect(results => {
expect(results).toBeTruthy();
})
.end(checkResult(done));
});
When I console.log(done) I get the following output: { [Function] fail: [Function] }
Below is our checkResult module.
//check-result
module.exports = function checkResult(done){
return function(seeIfThereIsError){
if(seeIfThereIsError){
done.fail(seeIfThereIsError)
} else {
done()
}
}
};
When an error occurs the if(seeIfThereIsError) block executes.
I have two questions:
When passing done into checkResult how does the returned function in checkResult's seeIfThereIsError argument get populated?
How is the signature { [Function] fail: [Function] } get created?
In short how could I create an arbitrary example from scratch to understand the working parts (syntax) on how this all fits together?

When passing done into checkResult how does the returned function in checkResult's seeIfThereIsError argument get populated?
That function is passed to and called by end. I.e. end will pass a value to the function. In its simplest form it would look like:
request.end = function(callback) {
callback(false);
};
How is the signature { [Function] fail: [Function] } get created?
How console.log represents functions isn't standardized. This output simply tells you that the value is a function object that has a custom property, fail, which is also a function.
If you want to create such a value yourself, this is how it could be done:
function done() {}
done.fail = function() {};
Whether console.log(done) gives you the output you saw depends on the browser and potentially other implementation specific heuristics.

Related

Why does RxJS subscribe allow omitting the arrow function and the following method argument?

Recently I needed to use RxJS. I tried to design an error handling flow, but I discovered some weird syntax passing method arguments:
.subscribe(
x => {
},
console.warn // <- Why does this compile, and warn 'is not 7' in debug console?
);
Link to minimal reproduction:
https://stackblitz.com/edit/rxjs-6-5-error-handle-no-arrow-issue
Steps to reproduce:
Use RxJS 6.5
Create a function return observable
Subscribe to observable
Pass parameter into subscribe
Just use ,console.warn, not like ,error => { console.warn(error); }
Without an arrow function, it still passes errors to console.warn. Why?
Code:
import { throwError, concat, of } from "rxjs";
import { map } from "rxjs/operators";
const result = concat(of(7), of(8));
getData(result).subscribe(
x => {
console.log("typeof(x)", typeof(x));
if (typeof(x) === 'string') {
console.log("x Error", x);
return;
}
console.log("no error", x);
},
console.warn // <- Why does this compile, and warn 'is not 7' in debug console?
);
// pretend service method
function getData(result) {
return result.pipe(
map(data => {
if (data !== 7) {
throw "is not 7";
}
return data;
})
);
}
I tried to google some keywords, js, rxjs, angular, omit arrow function, argument missing,... but I cannot locate what tech is used here.
Could anyone provide links to where this mechanism is explained?
The following two questions are related but do not explain the behavior, just say "equivalent":
The line
map(this.extractTreeData)
is the equivalent of
map(tree => this.extractTreeData(tree))
How to pass extra parameters to RxJS map operator
Why is argument missing in chained Map operator
First you need to understand what you're actually passing to the .subscribe function. Essentially it accepts three optional arguments next, error and complete. Each of it is a callback to be executed when the corresponding notification is emitted by the source observable.
So when you're using an arrow function, you define an in-place callback function.
sourceObservable.subscribe({
next: (value) => { },
error: (error) => { },
complete: () => { }
});
Instead you could define the functions separately and use it as callbacks.
onNext(value) {
}
onError(error) {
}
onComplete() {
}
sourceObservable.subscribe({
next: this.onNext,
error: this.onError,
complete: this.onComplete
});
Now this is what you're seeing. But instead of a user-defined function, you're passing the built-in console.warn() function. And in-turn the values from the notifications will be passed as arguments to the callback functions. So the value from your error is not 7 is sent as argument to console.warn() which then does it's job (i.e. prints to the console).
However there's a catch. If you wish to refer to any of the class member variables using the this keyword in the callback, it'll throw an error saying the variable is undefined. That's because this refers to the scope of the function in the callback and not the class. One way to overcome this is to use an arrow function (we've seen that already). Or use bind() function to bind the meaning of this keyword to the class.
sourceObservable.subscribe({
next: this.onNext.bind(this),
error: this.onError.bind(this),
complete: this.onComplete.bind(this)
});
So if you wish to only have the error callback for example, you could explicitly state it and ignore the others.
sourceObservable.subscribe({ error: console.warn });
Now as to your question "why no parentheses in the function call", it was discussed here and here. The arguments expect a reference to a function and the function names denotes their reference.
console.log is a function
function can be called with arguments in a bracket
console.log("123") means call function console.log with argument "123"
tree => console.log(tree) is also a function
it can also be called with arguments in a bracket, eg.
(tree => console.log(tree))(tree)
so a function with callback as its argument can call its callback with arguments in a bracket
function example(callback) {
callback();
}
so if we pass console.log to it, example(console.log), it basically runs as
function example(callback) {
console.log();
}
if we pass tree => console.log(tree) to it, example(tree => console.log(tree)), it basically runs as
function example(callback) {
(tree => console.log(tree))();
}
if you understood above code. it's easy to understand subscribe now.
function subscribe(nextCb, errorCb, completeCb) {
// ... got next data
nextCb(data);
//... got error
errorCb(error);
// completed observe
completeCb();
}
so your error callback console.log basically get called as console.log(error);
error=> console.log(error) basically get called as (error=> console.log(error))(error);
which in this case results are same.
In JS functions are first class objects. When you have the code console.warn no brackets you have a reference to this object but you're not invoking that object, that would require braces console.warn(). For example you can do:
let x = console.warn;
console.log('not happened yet');
x('test');
So your code is simple passing the console.warn function to the parameter of the Subscribe failure in exactly the same manner as you might pass any other function, e.g.
Subscribe(() => {}, () => {});
[why] show Warn 'is not 7'
The other part of this is that your throwing an error throw "is not 7";. The signature of the error call of Subscribe is thus:
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;
So the parameter of error is of type any. So the throw passes an Error to the error function handler. This is set as console.warn which has a signature of :
console.warn(obj1 [, obj2, ..., objN]);
console.warn essentially turns whatever parameter it's passed into a string, JS is not strongly typed and this is essentially down to type coercion, and logs it. the string of throw "is not 7"; is is not 7. So it logs is not 7.
All in all I'd say this is all all a bit cryptic and potentially difficult to follow. There is nothing technically wrong here, but I would say it would make more sense to do the following:
.subscribe(
x => {
},
x => {console.warn(x);}
);
Based on the principle that "Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
This happens due to the three possible types of value an Observable is able to emit,
next
error
complete
These logic is translated in the arguments of the subscription function, so the first function callback will trigger the values emitted via next, the second callback the values emitted with error and the third function with the complete value.
In you case, console.warn is passed to the second function as a function that will be called each time an error is emitted.
For the second question you can refer to the arrow function docs, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Your subscribe expects 2 arguments. The first argument is a function that will be called with the "next" values and the second argument is again a function that will be called with if there occured an error. So, since "console.warn" is a function, you can just use it as a second argument.
(error) => console.warn(error) is the same as console.warn(error)
But be careful if since console.warn does not rely on the context "this", you will give no issues. But if you want to use a function that uses the context "this", you will need to use an arrow function.
For more info about JS scoping: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

JavaScript Callback Functions.. confused

I'm sure the answer to this question exists somewhere, but unsure how to phrase the question and don't find what I'm looking for when I try to research callbacks.
Ok, so I've just started dabbling with Gulp for compiling my CSS, and I think it's great!.. despite being useless with JavaScript.
My code is below, but I don't understand how these callbacks are working. How can callbacks be set as a parameter and then be called from inside the function? I don't get it.. is the function basically expecting something to be in there? What value is being set or what's expected to run? I can't seem to make sense of it.
I see this quite frequently in JavaScript, but unsure how it works. I've looked up videos and tutorials of functions and callbacks, they makes sense, but I never seem to be able to find anywhere where this concept is explained which makes me believe I'm not looking for the right thing.
I see the same sort of thing with Promises as well where 'resolve' or 'reject' parameters are set, but unsure what's going on when a condition is met, or what values are set and where.. hope this makes sense and appreciate any help in understanding this better.
const gulp = require('gulp');
const sass = require('gulp-sass');
const browserSync = require('browser-sync');
// TASKS
// Compile SASS
gulp.task('sass-compile', (callback) => {
gulp.src('scss/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('css'))
.pipe(browserSync.stream());
console.log('******************** SCSS > CSS successful ********************');
callback();
});
// Live reload
gulp.task('browser-sync', (callback) => {
browserSync.init({
proxy: 'http://localhost/test1',
port: 80
});
callback();
});
// WATCHER
gulp.task('default', gulp.series('browser-sync', (callback) => {
gulp.watch('scss/*.scss', gulp.series('sass-compile'));
callback();
}));
function foo(baz) {
console.log("Foo: " + baz);
}
function other(bar) {
const arg = "Bar";
bar(arg);
}
other(foo);
I can see how this is useful when it comes to performing asynchronous tasks. But its quite counter intuitive and I can immediately see the potential for type errors.
I've since changed my coding style to use ASYNC/AWAIT instead as much as possible. But it still a pain to navigate legacy code. Hence, the reason why I ended up looking up this thread. Haha
Functions are a type of object. Objects are a type of value.
Values can be stored in variables, in object properties, and passed as arguments to functions.
So:
Here is a simple function, which is called.
function foo() {
console.log("Foo");
}
foo();
Here is a simple function, copied to a different variable, which is called.
function foo() {
console.log("Foo");
}
const bar = foo;
bar();
And now passed as an argument to another function.
function foo() {
console.log("Foo");
}
function other(bar) {
bar();
}
other(foo);
And the same, but with arguments passed to the original function.
function foo(baz) {
console.log("Foo: " + baz);
}
function other(bar) {
const arg = "Bar";
bar(arg);
}
other(foo);
You've just been looking at examples where the function responsible for calling the callback is not code you've written (and you aren't looking at the source code of the function which calls the callback).
In javascript, functions are first class objects, which means they can be passed around to other functions. There are many cases where this can be used, but the cases you're referring to (gulp.task and new Promise) are essentially using it to do two-way communication between your code and their code. When you use gulp.task, basically the following steps are happening
1) You call gulp.task, saying "i'd like to do some work please". You pass in a function saying what you'd like to do
2) at the appropriate time, gulp.task calls your function, but passes in to it another function saying "when you're done, call this to let me know".
A simplified version of gulp.task would look like this:
const task = (name, workToDo) => {
const done = () => {
console.log('task', name, 'has let me know its done');
}
console.log('ive been asked to start task', name);
setTimeout(() => {
console.log('500ms elapsed; starting task', name);
workToDo(done);
}, 500);
}
task('sample', (done) => {
console.log('doing work');
setTimeout(() => {
console.log('1 second has elapsed. calling back to let it know i am done');
done();
}, 1000)
});
The constructor for a promise has a similar purpose. You say "i'd like to create a promise, using the following code", and then it calls your code passing in two functions that it just created (usually named resolve and reject). These are used to tell the promise "i'm done successfully" or "i'm done with an error", and thus move the promise to a resolved state or a rejected state.

Return result aSync function in method

First of all, I completely read all the answers on this question, but despite that I come to realize that after years of scripting I ended up in the aSync hell.
I have a method that uses an async function. Depending on the result of that function, the method should return true or false.
So, simply said:
example = {
overview: undefined,
aSyncFunction: function (callback) {
// Adds values to overview, which we will use in otherFunction
callback();
return this;
},
otherFunction: function (data) {
var result = false;
this.aSyncFunction( function () {
var available = this.overview[data.name];
// result == filter overview with supplied data)
}.bind(this));
return result;
}
};
I've created a JsFiddle to show you the exact situation I'm in: https://jsfiddle.net/copt436a/1/
Deleting the setTimeOut will deliver true, otherwise it is false.
Note, I'm cannot change aSyncFunction at the moment, so the solution must be somewhere in otherFunction
I tried to separate the callback function in a different function, but in that case the return value is stuck in that particular function otherFunction keeps returning undefined. Also using the return value of aSyncFunction does not give me the result I want, cause this returns this.
I'm completely stuck on this one, and probably the solution is quite simple.

Node.js async module waterfall - dynamically load and execute functions

I am attempting to dynamically load a series of anonymous functions and execute them, passing the results of the previous to the next.
Here is an example function:
module.exports = function (data) {
// do something with data
return (data);
}
When the functions are loaded (they all sit in separate files) they are returned as an object:
{ bar: [Function], foo: [Function] }
I would like to execute these functions using async.waterfall. This takes an array of functions, not an object of functions, so I convert as follows:
var arr =[];
for( var i in self.plugins ) {
if (self.plugins.hasOwnProperty(i)){
arr.push(self.plugins[i]);
}
}
This gives:
[ [Function], [Function] ]
How can I now execute each function using each async.waterfall passing the result of the previous function to the next?
SOLUTION
Thanks to the comments from #piergiaj I am now using next() in the functions. The final step was to make sure that a predefined function was put first in the array that could pass the incoming data:
var arr =[];
arr.push(function (next) {
next(null, incomingData);
});
for( var i in self.plugins ) {
if (self.plugins.hasOwnProperty(i)){
arr.push(self.plugins[i]);
}
}
async.waterfall(arr,done);
If you want them to pass data on to the next one using async.waterfall, instead of returning at the end of each function, you need to call the next() method. Additionally, you need to have next be the last parameter to each function. Example:
module.exports function(data, next){
next(null, data);
}
The first parameter to next must be null because async.waterfall treats it as an error (if you do encounter an error in one of the methods, pass it there and async.waterfall will stop execute and finish passing the error to the final method).
You can then convert it like you already to (object to array), then call it like so:
async.waterfall(arrayOfFunctions, function (err, result) {
// err is the error pass from the methods (null if no error)
// result is the final value passed from the last method run
});

async.js - right method for probing

I have doubts on selecting async.js method for probing several alternatives and stop when the first of them is successful.
For example:
async.probeSeries([
function (callback) {
// try something and call callback
// without arguments - probing fails
callback();
},
function (callback) {
// try something and call callback
// with arguments - probing successful
callback(null, ok);
},
function (callback) {
// will be not executed, because
// the second one is successful
callback();
}
], function (err, result) {
// handle the result returned by the second probe
});
I think that using series and return the result as error way may be a workaround but is there a better way?
Maybe you are looking for detectSeries? It works a littlebit different than your example as it checks values from an array with the same function and then callbacks with one of these values, but maybe you can apply it to your problem.
Btw, this looks like the perfect use case for a promise library like Q, where you'd write
probe1().fail(probe2).fail(probe3).done(resulthandler, errhandler);

Categories