i have a simlple asynchronious reader in a node-script reading firefox-json-bookmarks.
when i remove the first parameter (err) in the callback function, i got an error.
what is the reason? And why is err different from e?
app.js
var fs = require('fs'), obj;
fs.readFile(__dirname + '/bookmarks.json', 'utf8', handleFile);
function handleFile( err, data ) { // why is the first parameter needed?
try {
obj = JSON.parse( JSON.stringify(data) );
console.log(obj);
} catch (e) {
console.log(e);
//console.log(err);
}
}
Each time you call a function, that function is pushed onto a stack of functions known as the call stack. When that function returns a value, it is popped off the stack. The call stack describes where you are in your program and how you got there.
Synchronous Code
Imagine the call stack through out the course of this program.
function a() { return b() }
function b() { return c() }
function c() { throw new Error() }
a();
First, a is called, so we add it to the stack.
[ a ]
Then a calls b, so we add that to the stack too.
[ a, b ]
Then b calls c.
[ a, b, c ]
Then c throws an error. At this point, the debugger can tell you that c was the function that threw the error and that you ended up at c, via a then b. This works fine for regular synchronous code, like JSON.parse.
Asynchronous Code
Asynchronous code continues to run after the function has returned. For instance:
function a() {
setTimeout(function() { console.log(2); }, 10000);
return 1;
}
If you call a, then it will be pushed onto the call stack, then return 1 and be popped off the call stack. About 10 seconds later 2 will be printed into the console.
What would happen if the timeout did this instead?
function a() {
setTimeout(function() { throw new Error(); }, 10000);
return 1;
}
The error would be thrown, but the call stack would be empty. As you can imagine, this isn't very useful for developers.
This is also a problem if we want to return a value asynchronously. By the time the async stuff happens (timeout, read/write, network etc), the function has already returned.
Instead, we can use a form of what's known as Continuation-Passing Style, mostly referred to as callbacks. As well as calling our asynchronous function, we also pass it function (a continuation), which we ask it to run when it has finished. Remember, this can be after the function has returned a value!
In Node.js, these callbacks serve two purposes:
Errors
If an error occurs whilst doing the asynchronous work, standard practice is to call the callback with the error as the first argument. You'll often see the following code.
foo.doAsyncBar(function(err, baz) {
if(err) throw err;
// do something with baz
});
By passing the error the callback rather than throwing it, we are able to make our own decisions about how best to handle it. We can just throw it, as shown above, or we can handle it in a more graceful way.
Return
Hopefully, the function won't error, in which case, the general practice is to pass null as the first argument to the callback. This lets the developer writing the handling code know that the function didn't error and that the return value is in one of the next arguments.
For a more in depth article on Node.js error handling, see Joyent's Production Practices document for Errors.
Related
I recently tried to figure out how promises works in ECMAScript. Most interested in the construction of AwaitExpression. In my opinion, it is the most incomprehensible and rather complicated in the specification.
Let me give some code:
/// Promise
var promiseA = new Promise((resolve, reject) => {
setTimeout(() => resolve("Done!"), 10000)
});
/// Async/Await
(async function(){
var result = await promiseA;
console.log(result); /// Output: "Done!"
})();
/// Promise.prototype.then
promiseA.then(function (result){
console.log(result); /// Output: "Done!"
});
For me, as I said above, AwaitExpression is incomprehensible, I do not understand where the return of the value from promise is going. But I understand where the value from [[PromiseResult]] comes from and how the value [[PromiseResult]] from .then is passed into the argument to the callback function.
And there are some steps that unfortunately are not clear to me from Await ():
Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context
stack as the running execution context.
Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion completion, the following
steps of the algorithm that invoked Await will be performed, with
completion available.
Return.
NOTE: This returns to the evaluation of the operation that had most previously resumed evaluation of asyncContext.
And part of the actions from the Await Fulfilled Functions is still not quite clear:
Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended
it.
Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the
currently running execution context.
Return undefined.
P.S How Promise and .then are executed is clear to me, you can take this into account when explaining AwaitExpression.
I think the critical thing to keep in mind when reading this is that the spec is free to start and stop execution of a given function right in the middle. When that happens, the function literally stops exactly where it is, but the spec steps keep going.
Given your example
(async function(){
var result = await promiseA;
console.log(result); /// Output: "Done!"
})();
in spec terms, this functions becomes:
Call the async function [[Call]]
Do the normal prep work that function has
OrdinaryCallEvaluateBody
EvaluateBody for async functions
Create the promise that gets returned by the async function
AsyncFunctionStart
Initialize the promise object that gets returned
Mark the execution context such that, when it fully completes, it will fulfill or reject the promise.
Start executing the steps of the function, one at a time
Eventually we get to the await and do:
Using the awaited promise, set it up so that the async function will resume execution when the promise finishes. (Step 2-9)
"pop the execution context" which marks what step we were in the function, so that it can be started again later. (Step 10 in your quote)
Set a flag on the execution context saying "the function is suspended, when it resumes, you will get the result of a promise, please resume the function as if the promise result was the completion value of the 'await'. (Step 11)
Return. (Step 12).
Return the promise for the async fn result.
The core thing to understand, is that Step 12 is not returning into the async function, because it was suspended. It is returning to AsyncFunctionStart, which then returns to EvaluateBody, which then returns the Promise result of the async function.
Then, later when the promiseA fulfills (due to the work of step 2-9 earlier)
Set the async function as the active execution context again (Step 5 of "Await Fulfilled Functions")
Resume execution with the result of the promise as the "completion" (Step 6). Because of the flags set on Step 11, this uses the completion value as the result of the await, and picks up execution of the async function until it returns, or awaits again.
Assert that the async function has finished some work for now (Step 7)
Return. (Step 8)
It's the part
using NormalCompletion(value) as the result of the operation that suspended it.
from the text you quoted. It will resume the execution of the async function, and make the await expression have the value as the result. Compare the yield operation and the generator next() method for reference.
I have a simple program that consumes IMDB api, I'm getting the result, but it was shown as error because the result is not a structured json.
MovieService.ts
export class MovieService {
constructor(private http:HttpClient) { }
getMovie(movie:string){
return this.http.get(this.generateURL(movie));
}
private generateURL(movie:string){
return "https://v2.sg.media-imdb.com/suggests/titles/"+movie.charAt(0)+"/"+movie+".json?callback=imdb$"+movie;
}
}
addmovie.component.ts
private _filterMovies(value: string) {
this.movieService.getMovie(value).subscribe(
movies => {
console.log(movies);
return movies;
}
);
}
ngOnInit() {
this.addMovieForm.get('movie').valueChanges.subscribe(val => {
this._filterMovies(val)
});
}
I'm getting error like
the response is of bad json. How can I format the json upon receiving? How to solve this? Any leads would be helpful.
The result is not JSON, but rather JSONP. It is essentially returning you a script that is trying to execute the callback method specified.
Instead of http.get() you should call http.jsonp(url, "imbdIgnoresThisParam").
However, according to this answer, the callback query string parameter is ignored by IMDB. The answer suggests dynamically creating the expected callback function, whose name contains the title for which you are searching. In that callback you could do a few different things.
Use the closure to call / set something in your MovieService. This will result in your call to the API throwing an error, as the Angular framework's callback will not be called as expect. You could ignore the error.
Try to call the expected Angular callback, ng_jsonp_callback_<idx>. This will prevent the API call from throwing, but it may not be reliable. The callback name is dynamic and increments with each jsonp() call. You could try to track the number of jsonp() calls in your app. And of course, the framework may change and break this solution. Concurrent calls to getMovie() may break, as the next one may step on the previous callback on the window. Use with caution!
In typescript, your getMovie() function and related helpers might look like so:
private imdbData: any = null;
private jsonpIdx = 0;
private setImdb(json: any) {
this.imdbData = json;
// or do whatever you need with this
}
getMovie(movie:string) {
// dynamically create the callback on the window.
let funcName = `imdb$${movie}`;
window[funcName] = (json: any) => {
// use the closure
this.setImdbData(json);
// or try to call the callback that Angular is expecting.
window[`ng_jsonp_callback_${this.jsonpIdx++}`](json);
}
// go get your data!
let url = this.generateURL(movie)
return this.http.jsonp(url, "ignored").subscribe((json) => {
// this happens if you successfully trigger the angular callback
console.log(json);
}, (err) => {
// this happens if the angular callback isn't called
console.log(this.imdbData); // set in closure!
});
}
Edit for Angular 4
For Angular 4, it looks like you will need to import the JsonpModule along with the HttpModule. Then, you'd inject jsonp just like you'd inject http into your service. The call to IMDB becomes this.jsop.request(url).subscribe(...) and your dynamic callback name needs to change, too.
window[funcName] = (json: any) => {
// or try to call the callback that Angular is expecting.
window["__ng_jsonp__"][`__req${this.jsonpIdx++}`]["finished"](json);
}
I don't have an Angular 5 or 6 project immediately set up, so hard to say if there are any differences with the callback in those versions.
Sort of a hack, but hope it helps!
I've just recently finished working on a rather complex contract with the Remix IDE. I'm now attaching web3 to the frontend but when I call functions that should fail, they still go through on Metamask.
When testing my contract in Remix, I would often click on and call certain functions that had require statements that I knew would fail just to confirm that the contract state was recorded correctly. Remix didn't send the transaction to metamask and instead output an error message and I would like to handle the transaction error on my own as well.
How can I check my contract call to see whether it will fail. Must I use the method that predicts gas and detect it that way and if so how? My current code is below:
contract.callFunction(function(error, result) {
if (!error) alert(result);
else alert(error);
}
The above code catches rejecting the metamask confirmation as an error but transactions that should fail go through to metamask with an insanely high gas limit set. The function callFunction is in the contract and takes no parameters but does have an effect on the blockchain so it requires the transaction. The first line of the function is "require(state == 1);" and I have the contract set to state 2 currently so I'm expecting the transaction to fail, I just want to detect it failing.
In order to find out whether the transaction will fail we do have to call estimateGas() and attach a callback function. I assumed we'd have to check the gas estimate returned in order to predict whether it would fail but the process is made rather easy. Here's the full code I ended up with to successfully run a function while catching the two most common error cases.
contract.nextState.estimateGas(function(error, result) {
if (!error) {
contract.nextState(function(error, result) {
if (!error) {
alert("This is my value: " + result);
} else {
if (error.message.indexOf("User denied") != -1) {
alert("You rejected the transaction on Metamask!");
} else {
alert(error);
}
}
});
} else {
alert("This function cannot be run at this time.");
}
});
[EDIT] I'm coming back after the fact to help clear up information for those with a similar question. All of the information discussed below references the following link.
After creating a contract object, you can access any variable or function through using it's name. You can also access these members through array notation which is useful when the name of the variable or function isn't known at the time the code is written.
contract.foobar == contract["foobar"]
Once you have a function object (contract.foobar) you can use either call, send, or estimateGas. After first giving the function the parameters it needs (call it like any other function) you then use either call, send, or estimateGas on the returned object while providing options and a callback function.
This callback function takes 2 parameters. The first is the error which will be undefined if there was no error, and the second will be the result of the call, send, or estimateGas. Call and Send will both return the result of the function while estimateGas always returns a number showing how much gas is estimated to be necessary.
I have the following service in Angular2:
#Injectable()
export class MyService{
private myServiceUrl= ....
constructor(private http: Http) { }
getService(): Promise<MyObject> {
return this.http.get(this.myServiceUrl).map(response => response.json())
.toPromise();
}
}
And I have this function in one of my components:
myFunction(): any{
let toReturn: any;
this.myService.getService().then(result => toReturn = result);
console.log(toReturn);
return toReturn;
}
Basically, as you can see, I want to store in toReturn, the objects that it is returned by the promise of myService. I am looking to the rest call I have, and I am getting the proper json, but when I am trying to store it in the internal variable, I get undefinied in toReturn.
However, if I try this:
this.myService.getService().then(result=> console.log(result));
I am able to see the json I want.
I can do a function like this:
getService(address: string) {
this.myService.getService().then(result=> this.result= result);
}
But I prefer to make my function to return an object. What am I doing wrong?
Remember that getService is asynchronous, so when your code reaches the line
return toReturn;
The toReturn variable has not yet received the data coming from the server.
The cleanest way to deal with this is to return the promise itself, so that the calling code will extract the data when it arrives from the server:
myFunction(): Promise<MyObject>
{
//return the promise that will output data in the future
return this.myService.getService();
}
The calling code can access the data this way:
myFunction().then( result =>
this.result= result
)
You can even remove a step by getting rid of myFunction because it's just a thin wrapper around getService(). Instead the calling code can simply do:
this.myService.getService().then(result => this.result = result)
And that's it.
Addendum to address your comments:
You must understand two concepts about asynchronous ops and promises:
A promise is executed in a different thread, so the lines after the call to getService() are executed before the data arrives. That's why console.log() doesn't show you data at that point. That's also why trying to return the result there doesn't work
the only place in code where you can capture the result of a promise is .then(). There you can do whatever you want with the result, including storing it in any variable. Just remember point 1 above, and don't expect to have access to the result on the very next line because then() occurs later in time on a different thread.
assign the json that comes from that service (which returns a
promise), to an internal variable that I defined in the line above, it
is not working. How can store it to an internal variable and return it
You cannot.
If I set up a function that accepts a callback:
function loadSomething(path:String, callback:Function):void;
And that callback should accept a given type, for example a String to represent some loaded information:
function onLoaded(response:String):void;
// Load some data into onLoaded.
loadSomething("test.php", onLoaded);
Is it possible to assess the function that will be used for callback and ensure that it has both a given amount of arguments and that the argument accepts the correct type? e.g.
function broken(arg:Sprite):void;
// This should throw an error.
loadSomething("test.php", broken);
I don't think you should bother doing this kind of check as it would create an uncessary overhead. You can simply throw the exception when you do the callback:
try {
doCallback(response);
} catch(e:*) {
trace('Incompatible callback');
}
If you really want to do the check, you might be able to do it using reflection. Just call describeType(callback) from flash.utils and parse the XML.
One simple thing you can do is to check the number of acceptable arguments by calling length property on method closure like:
function some ( val1 : int, val2 : int ) : void { return; }
trace(some.length); // traces 2
Other much more complex method maybe is to use AS3Commons bytecode library. You can experiment with dynamic proxies.