How do functions like fs.writeFile() callback arguements work? - javascript

I'm learning JavaScript at the moment, specifically Node JS. And during a basic tutorial I notice that some functions have callbacks with predefined arguments. For example
fs.writeFile(
path.join(__dirname, 'test', 'hello.txt'),
"Hello World",
err => {
if (err) throw err;
console.log('File Created...')})
I want to know where was "err" defined as a ErrNoException type.
Thanks!

You can write similar functions yourself. Callback is simply a function (F) which is passed to another function(A) as a parameter. Function (A) executes some tasks and then executes Function F from parameters.
For example:
function getData(url, callback) {
fetch(url)
.then(res => callback(null, res))
.catch(err => callback(err, null))
}
In the sample function, what happens is, after making a fetch request, we wait until a response or error is generated. If a response is there, we simply pass that to callback param 2 and call first param to null and vice versa.
Now, while calling the function,
getData('http://stackoverflow.com', (err, res) => {
if(err)
return console.log(err)
console.log(res)
})

I want to know where was "err" defined as a ErrNoException type.
In this type of interface, you pass a callback function and fs.writeFile() determines what arguments it will pass that callback when it calls it. It is up to you to declare your callback function to have appropriately named arguments that match what the caller will be passing it. So, YOU must declare the callback with an appropriately named argument. Since Javascript is not a typed language, you don't specific what the type of that argument is. The type of the argument is determined by the code that calls the callback. In this case that is the fs.writeFile() implementation. To use the callback appropriately, you have to read the documentation for fs.writeFile() so that you know how to use the argument to the callback.
So, in this specific case, fs.writeFile() requires a callback function and that callback function will be passed one argument and that argument will either by null (if there was no error) or will be an Error object that explains what the error was). You must learn this by either reading the code for fs.writeFile() or reading some documentation for it.
It's important to realize that when you define your callback function, all you are doing is providing a name for the arguments you expect the callback to be called with. You can then use that name in your code to reference those arguments. The actual data that is passed to that callback when it is called will be supplied by the fs.writeFile() function when it calls the callback.
It's unclear exactly what you mean by "defined as a ErrNoException type". Nodejs has an internal function (not available for external use) called errNoException() and you can see the source here. It uses that function internally for creating an Error object with some common properties such as an error code and a stack trace.

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

d3.queue never triggering .await function

I've run into a problem using d3-queue. This is my code:
var dataQueue = d3.queue();
dataQueue.defer(collectData,ISBNs,locations)
.await(processData);
Where collectData is a function that does several API calls (a large number of them to the Google Books API).
Now the problem is that the processData function is never called. I know for a fact that the collectData function runs properly, since I put a print statement just before the return statement, along with several other print statements along the way.
You are not passing your data correctly between the deferred task collectData and the final processData. The documentation has it as follows (emphasis mine):
# queue.defer(task[, arguments…]) <>
Adds the specified asynchronous task callback to the queue, with any optional arguments. The task is a function that will be called when the task should start. It is passed the specified optional arguments and an additional callback as the last argument; the callback must be invoked by the task when it finishes. The task must invoke the callback with two arguments: the error, if any, and the result of the task.
Thus, to pass the result of the deferred task to the function processData, your function collectData() has to be something like this:
function collectData(ISBNs, locations, callback) {
var error = null; // The error, if any
var data = { }; // The actual data to pass on
// ...collect your data...
// Pass the collected data (and the error) by invoking the callback.
callback(error, data);
}

Async each function

I'm trying to understand async each(coll, iteratee, callback) function to execute a function in parallel for each item of an array. From the async docs I understand that callback will be executed only once (when iteratee function will be executed for each item of the array).
And in the case of an error in the iteratee function, calling callback('some error message') will immediately call the callback function with error message.
Below is an example from the async docs for each function
each(coll, iteratee, callback)
// assuming openFiles is an array of file names
async.each(openFiles, function(file, callback) {
// Perform operation on file here.
console.log('Processing file ' + file);
if( file.length > 32 ) {
console.log('This file name is too long');
callback('File name too long');
} else {
// Do work to process file here
console.log('File processed');
callback();
}
}, function(err) {
// if any of the file processing produced an error, err would equal that error
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('A file failed to process');
} else {
console.log('All files have been processed successfully');
}
});
What I'm not able to understand is, what does calling callback() without argument does, it looks very strange to me that we call callback() with no argument when there is no error in the iteratee function. What does calling callback() or callback(null) do in case of no errors.
Can't we just remove those callback() or callback(null), when we actually mean to call the callback only once (when iteratee function is executed for all the elements of the array) rather than for each item of the array.
What does calling callback() or callback(null) do in case of no errors.
Calling callback with no arguments or with null signals to async.each that the iteratee function finished executing on that item (file in the case of the example). When all of the iteratee functions have called their respective callback, or one of them passes an error to it's callback, async.each will call the original callback function passed to it.
To elaborate on that a little, async.js is designed to handle asynchronous functions. The benefit (or issue, depending on how you look at it), of an asynchronous function is that there is no way to tell when it will finish executing. The way to deal with this is to pass the asynchronous function another function, a callback, to execute when it is finished. The asynchronous function could pass any errors it encounters, or any data it retrieves, to the original calling function through the callback function. For example, fs.readFile passes the read file data, and any errors, through the callback function is it passed.
Can't we just remove those callback() or callback(null), when we actually mean to call the callback only once (when iteratee function is executed for all the elements of the array) rather than for each item of the array.
No, because async.js has to assume the iteratee function is asynchorous, and therefore, it has to wait for it to its callback. The callback passed to async.each is only called once.
The confusion may be caused by the variable names. An individual callback function is only meant to be called once. The callback function passed to async.each is not the same callback passed to the iteratee. Each time iteratee is invoked with a value in coll, it is passed a new callback function. That call of iteratee is only supposed to call the passed callback once (async will throw an error otherwise). This allows async to track if a call to the iteratee function has called its callback, and wait for the rest to call their respective callback. Once all of the callback functions are called, async.each knows all of the asynchorous iteratee function calls have finished executing, and that it can call the original callback passed to it.
This is one of the difficult aspects of creating docs. They have to concise enough that a developer can get information from them quickly, and also include enough detail that they can explain the concept, or the function. It's a hard balance to achieve sometimes.
Calling callback with no arguments adds to a counter inside of the .each function. This counter, when full is the thing that actually calls your callback. Without that, it would never know when it completed.

How in Javascript (or in Node), the function's arguments are recognized?

I was learning Node.js (even though I am not an expert in Javascript but I understand and write code on it). Now, trying to play with Node.js, I just got stuck in this code:
var fs = require('fs');
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
console.log(data);
});
Here is some of my confusions:
The anonymous function is taking two arguments: err and data and inside the function err is for any error while reading the file which is thrown and data is the actual contents of the file.
Well, how the function knows and differentiate between which one is error and which one is content of the file?
Does it do like: the first argument is always an error and second argument is always the content data?
what err and data will have inside the function if I write like this
function(data, err) {} ?
This is just a simple function being passed just two arguments. How it works for some more arguments?
How inside the function, data is data and err is error?
For the above example, are err and data are pre-defined keywords (I doubt NO)?
The arguments will get values in the order they're passed while calling the function. You can see what's the correct order by reading the API docs for the function you plan to use.
So, if you change your function to function(data, err) {}, then data will contain the error, while err will hold the data :)
To ease your future work, nearly every Node.js function which accepts an callback, the 1st arg will be the error and the 2nd arg the return of the function.
1). Well, how the function knows and differentiate between which one is error and which one is content of the file?
The order of the arguments is known when the function is invoked from Node.
2). Does it do like: the first argument is always an error and second argument is always the content data?
Yes.
3). what err and data will have inside the function if I write like this
function(data, err) {} ?
They will have the same values, they simply won't be used.
For the above example, are err and data are pre-defined keywords?
Nope. You could even rename them and they will still be bound to the same values.
this is how it is defined in API
check here
as pointed by #gustavohenke if you define it like
function(data, err){ ... }
then data will hold error log and err will hold file data
EDIT
hope you read this in docs it should have cleared your doubt:
The callback is passed two arguments (err, data), where data is the contents of the file.

Node.js callback

I can't seem to grasp the concept of a callback. I haven't worked with them before so bear with me. To get my hands wet, I'm trying to login to twitter with zombie.js.
Here is an example:
var Browser = require("zombie");
var browser = new Browser({ debug: true})
browser.visit("https://mobile.twitter.com/session/new", function (callback) {
browser.fill("username", "xxxxx");
browser.fill("password", "xxxxx");
browser.pressButton("Sign in", function (err, success) {
if(err){
console.log(browser.text('.message'));
console.log('There has been a error: ' + err);
}
else{
console.log('Worked!');
}
});
});
At the browser.pressButton part, it will determine if I have been able to successfully login or not, depending on if .message contains the text "Typing on your phone stinks, we know! Double-check your username and password and try again."
However, I don't understand how it determines to fire the callback err. If .message isn't present in the html, then I would like to trigger the success callback to move onto the next function.
The convention Zombie seems to use for callbacks comes from node.js where the first argument is an error object, which should be null on success, and any subsequent arguments are for the success case. If you define a callback, the library you are using (Zombie in this case) will execute your callback function when their async operation is complete. When your callback is invoked it means "OK, an operation has completed and you can now process the result as you see fit". Your code needs to look at that first argument to decide if the operation was a success or failure.
When you accept a callback function as an argument and then perform some (possibly asynchronous) operation, the callback is the way for you to tell the calling library you are done, and again use that first argument to distinguish errors from success.
Part of your confusion is probably coming from the fact that your function signature for the callback to browser.visit is wrong. You need to name that first argument to clearly indicate it's an error like this:
browser.visit("https://mobile.twitter.com/session/new", function (error, browser) {
So in the body of that anonymous callback function, if zombie couldn't load that page, the error argument will have info about the error. If the page did load correctly, error will be null and the browser 2nd argument can be used to further tell zombie to do more stuff on the page. This is how Zombie says "I'm done with the visit operation, time for you to handle the results."
visit doesn't pass a callback argument, the anonymous function you pass as an argument to visit IS THE CALLBACK. You could code it like this to clarify (although nobody does)
browser.visit("https://mobile.twitter.com/session/new", function callback(error, browser) {
So that's a callback when a library needs to tell you it is done. You don't invoke it. The library invokes it and you put code inside it.
On the other hand, when your code does async operations, you need to invoke the callback that you received as a function argument appropriately to tell your caller that you are done and whether it was success for failure. In this case, you don't do any of your own async code, so there's no need for you to invoke any callback functions.

Categories