JavaScript . Keeping in mind the order of execution in this language is a bit difficult (this is the case that is called “ Callback Hell ” or “ The Pyramid of Doom ”), if you have dealt with synchronous programming before. My usual answer was “you have to deal with it somehow.” After all, do we expect all programming languages to look and feel the same? Of course not.ECMAScript 6 , which describes the generators - the possibility of a language that will completely change our way of writing both server-side and client-side JavaScript . With the help of generators, we can turn nested callbacks into a similar synchronous code without blocking our only event loop . setTimeout(function(){ _get("/something.ajax?greeting", function(err, greeting) { if (err) { console.log(err); throw err; } _get("/else.ajax?who&greeting="+greeting, function(err, who) { if (err) { console.log(err); throw err; } console.log(greeting+" "+who); }); }); }, 1000); sync(function* (resume) { try (e) { yield setTimeout(resume, 1000); var greeting = yield _get('/something.ajax?greeting', resume) var who = yield _get('/else.ajax?who&greeting=' + greeting, resume) console.log(greeting + ' ' + who) } catch (e) { console.log(e); throw e; } }); Chrome Canary 33.0.1716.0 . Examples, except for those with XHR , should work in Node.js with the --harmony flag (from version 0.11, approx. Transl. ). The generator implementation proposed in JavaScript 1.7+ does not follow ECMAScript 6 draft — so you will have to make some changes to make the examples work in Firefox . If you want to run these examples in Canary , you can run them in the same way as here.ES6 generators and what they allow you to do.ECMAScript 6 draft, generators are “first-class coroutines that are objects that encapsulate pending execution contexts.” Simply put, generators are functions that can stop their execution (using the yield keyword) and continue their execution from the same place after calling their next method. JavaScript still performs only one task at the same time, but it is now able to pause execution in the middle of the body of a generator function and switch the context to something else. Generators do not allow parallel execution of code and they do not know how to handle threads. function* fibonacci() { var a = 0, b = 1, c = 0; while (true) { yield a; c = a; a = b; b = c + b; } } function run() { var seq = fibonacci(); console.log(seq.next().value); // 0 console.log(seq.next().value); // 1 console.log(seq.next().value); // 1 console.log(seq.next().value); // 2 console.log(seq.next().value); // 3 console.log(seq.next().value); // 5 } run(); run function initializes the Fibonacci number generator (it is described by the special syntax funtion* ). Unlike the usual function, this call does not start the execution of its body, but returns a new object - the generator.run function calls the generator method next (a synchronous operation), the code is executed until it reaches the yield .yield stops the generator and returns the result to the outside. The operations following yield have not been performed at this point. The value (operand a for yield ) will be available outside through the value property of the result of the execution.next method, the execution of the code continues from where it stopped at the previous yield .while . No, it will be executed inside the loop until someone calls its next method.yield will not be executed until the generator continues. You can also pass an argument to the generator, which will be substituted instead of the yield on which the previous execution of the generator was interrupted. function* powGenerator() { var result = Math.pow(yield "a", yield "b"); return result; } var g = powGenerator(); console.log(g.next().value); // "a", from the first yield console.log(g.next(10).value); // "b", from the second console.log(g.next(2).value); // 100, the result "a" as the value property of the execution result. Then we continue execution by passing a value of 10 to the generator. Use the substitution to demonstrate what happens: function* powGenerator() { var result = Math.pow(----10----, yield "b"); return result; } yield and again suspends its execution. The value "b" will be available in the returned object. Finally, we continue execution again, passing in argument 2 . Substitution again: function* powGenerator() { var result = Math.pow(----10----, ----2----); return result; } pow method is called, and the generator returns the value stored in the result variable.AjaxJavaScript code. As it turns out, we can take some ideas from previous examples.sync function. It creates a generator, passing it the resume function and calls the next method on it to start its execution. When the generator needs an asynchronous call, it uses resume as a callback and performs a yield . When an asynchronous call performs resume , it calls the next method, continuing the execution of the generator and passing the result of the asynchronous call to it. // ************** // framework code function sync(gen) { var iterable, resume; resume = function(err, retVal) { if (err) iterable.raise(err); iterable.next(retVal); // resume! }; iterable = gen(resume); iterable.next(); } function _get(url, callback) { var x = new XMLHttpRequest(); x.onreadystatechange = function() { if (x.readyState == 4) { callback(null, x.responseText); } }; x.open("GET", url); x.send(); } // **************** // application code sync(function* (resume) { log('foo'); var resp = yield _get("blix.txt", resume); // suspend! log(resp); }); log('bar'); // not part of our generator function's body event loop ; we stop the generator and continue to execute the code located further after the next call. The future callback, which will be called on another tick, will continue our generator, passing it the desired value. try { firstAsync(function(err, a) { if (err) { console.log(err); throw err; } secondAsync(function(err, b) { if (err) { console.log(err); throw err; } thirdAsync(function(err, c) { if (err) { console.log(err); throw err; } callback(a, b, c); }); }); }); } catch (e) { console.log(e); } catch will never be executed due to the fact that the execution of the callback is part of a completely different callstack, in another tick of the event loop . Exception handling must be located inside the callback function itself. You can implement a higher order function to get rid of some duplicate error checks and remove some attachments using a library like async . If you follow the Node.js convention about the error as the first argument, you can write a common handler that will return all errors back to the generator: function sync(gen) { var iterable, resume; resume = function(err, retVal) { if (err) iterable.raise(err); // raise! iterable.next(retVal); }; iterable = gen(resume); iterable.next(); } sync(function* (resume) { try { var x = firstAsync(resume); var y = secondAsync(resume); var z = thirdAsync(resume); // … do something with your data } catch (e) { console.log(e); // will catch errors from any of the three calls } }); catch . And the exception that occurred in any of the three functions will not allow subsequent functions to be executed. Very good.genny like genny and gen-run give this API: they simply perform a number of asynchronous operations before continuing the execution of the generator. Example using genny : genny.run(function* (resume) { _get("test1.txt", resume()); _get("test2.txt", resume()); var res1 = yield resume, res2 = yield resume; // step 1 var res3 = yield _get("test3.txt", resume()); // step 2 console.log(res1 + res2); }); JavaScript for a long time. But now with the generators in the browser ( Firefox with JavaScript 1.7 and Chrome Canary few months ago) everything is changing. New execution control constructs make it possible to use a completely new programming style, one that can compete with the traditional nested callback style. It remains to wait for the ECMAScript 6 standard to be implemented in tomorrow's JavaScript engines.Source: https://habr.com/ru/post/210330/
All Articles