ASYNC programing in Javascript[turn]

Source: Internet
Author: User

This article starts with the asynchronous style and analyzes the techniques, problems, and solutions that have become asynchronous in JavaScript. Specifically, from the problems caused by the callback, and talked about the use of events, Promise, generator and other technologies to solve these problems.

The non-blocking of async is infinitely good?

Async, is the lifeline of JavaScript without threading the model. On top of that, the Reactor design pattern 1 is used.

JavaScript is all about "async" two sons. In both the browser environment and the node environment, most APIs separate the request (or message, call) from the return value (or the result) through an "event". and "events" can be separated from callbacks (Callback), for example,

var fs = require("fs");fs.readFile(__filename, function(e, data) {    console.log("2. in callback");});console.log("1. after invoke");

The FS module encapsulates a complex IO module, whose invocation results are told by a simple callback to the caller. Looks pretty good, let's look at Ruby's EventMachine :

require "em-files"EM::run do  EM::File::open(__FILE__, "r") do |io|    io.read(1024) do |data|      puts data      io.close    end    EM::stop  endend

Because the APIs in Ruby's standard library are all synchronous, only third-party APIs like Eventmachine can be supported asynchronously. In the actual style, the two are similar, for our example, the JavaScript version seems to be more brief and does not need to add additional third-party modules.

asynchronous mode, which is less lossy than threading mode, and is even 2 better than Java in some scenarios. And, non-blocking the API is node default, which makes Nodejs and its asynchronous callbacks a lot of applications.

For example, we want to find the dimensions of all the files in the current directory:

fs.readdir(__dirname, function(e, files) {//callback 1    if(e) {        return console.log(e);    }    dirs.forEach(function(file) {//callback 2        fs.stat(file, function(e, stats) {//callback 3            if(e) {                return console.log(e);            }            if(stats.isFile()) {                console.log(stats.size);            }        });    });});

A very simple task creates a 3-level callback. In the early days of node application explosion, a lot of applications were born in this style. Obviously, this style of code has the following risks:

    1. Code is difficult to read and maintain: After nesting multiple callbacks, the author is not aware of the function hierarchy.
    2. Potential call stack consumption: in JavaScript, it's much easier than you think to go beyond the maximum stack. Many third-party modules do not make asynchronous calls, but pretend to support callbacks, and the stack is at greater risk.
    3. You want more? The first two bars are enough ...

Many programmers, because the first to abandon Nodejs, and even give up JavaScript. As for the second article, the elimination of various hidden bugs and the optimization of performance loss are beckoning to the programmer.

Wait, you said I kept talking about node, and I didn't mention the situation in the browser? Let's look at an example:

/*glboal $ */// we have jquery in the `window`$("#sexyButton").on("click", function(data) {//callback 1    $.getJSON("/api/topcis", function(data) {//callback 2        var list = data.topics.map(function(t) {            return t.id + ". " + t.title + "/n";        });        var id = confirm("which topcis are you interested in? Select by ID : " + list);        $.getJSON("/api/topics/" + id, function(data) {//callback 3            alert("Detail topic: " + data.content);        });    });});

We try to get a list of articles and then give the user some interaction, let the user choose an article that they want to know more about, and continue to get the article details. In this simple example, 3 callbacks are generated.

In fact, the nature of asynchrony is the inherent style of the JavaScript language itself, regardless of the hosting environment. So, the problem with callbacks flying over the sky is the commonality of the JavaScript language.

Solution evented

JavaScript programmers may be one of the most creative of a bunch of programmers. There are a number of solutions to the callback problem. The most natural thing to think about is the use of event mechanisms.

Or the scene where the article was loaded before:

var topiccontroller = new Eventemitter (); Topiccontroller.list = function () {//a simple wrap for Ajax request $.getjson ("/api/topics", This.notify ("Topic:list"))    ; return this;}; Topiccontroller.show = function (ID) {//a simple wrap for Ajax request $.getjson ("/api/topics/" + ID, this.notify ("topic    : Show ", id)); return this;};    Topiccontroller.bind = function () {//bind DOM Events $ ("#sexyButton"). On ("Click", This.run.bind (this)); return this;}; Topiccontroller._querytopic = function (data) {var list = Data.topics.map (function (t) {return t.id + "." + t.t    Itle + "/n";    }); var id = confirm ("which Topcis is interested in?"    Select by ID: "+ list"; This.show (ID). Listento ("Topic:show", this._showtopic);}; Topiccontroller._showtopic = function (data) {alert (data.content);}; Topiccontroller.listento = function (eventName, listener) {//a helper method to ' bind ' This.on (EventName, Listener.bind ( this);}; Topiccontroller.notify = function (eventName) {//generatE a notify callback internally var = this, args;    args = Array.prototype.slice (arguments, 1);        return function (data) {args.unshift (data);        Args.unshift (EventName);    Self.emit.apply (self, args); };}; Topiccontroller.run = function () {this.list (). Lisento ("Topic:list", this._querytopic);};/ /kickoff$ (function () {Topiccontroller.run ();});

As you can see, the B-grid is much higher now. Various packages, all kinds of decoupling. First of all, in addition to universal jquery, we also rely on EventEmitter , this is an observer pattern implementation of 3, such as Asyncly/eventemitter2. A simple summary of this style:

    1. Eliminates most scenarios where anonymous functions are used as callbacks, up to 0 nested, and the code is brief
    2. Between each state (or step), use an event mechanism to correlate
    3. Each step is independent of each other to facilitate future maintenance

If you are hard to be picky, there are shortcomings;

    1. Overall process blur due to excessive separation
    2. Increased code volume and another maintenance cost
Higher order functions

With higher-order functions, functions can be recursively executed sequentially and concurrently.

We can write a higher order function that allows the order of incoming functions to execute:

var runInSeries = function(ops, done) {    var i = 0, next;    next = function(e) {        if(e) {            return done(e);        }        var args = Array.prototype.slice.call(arguments, 1);        args.push(next);        ops[0].apply(null, args);    };    next();};

Or our previous example:

var list = function(next) {    $.getJSON("/api/topics", function(data) { next(null, data); });};var query = function(data, next) {    var list = data.topics.map(function(t) {        return t.id + ". " + t.title + "/n";    });    var id = confirm("which topcis are you interested in? Select by ID : " + list);    next(null, id);};var show = function(id, next) {    $.getJSON("/api/topics/" + id, function(data) { next(null, data); });};$("#sexyButton").on("click", function() {    runInSeries([list, query, show], function(e, detail) {        alert(detail);    });});

It still looks good, concise and clear, and the final amount of code does not increase. If you like this way, take a look at Caolan/async and find out more.

PROMISE

A promise represents the eventual result of an asynchronous operation. The primary-interacting with a promise is through it then method, which registers callbacks to receive either a PR Omise ' s eventual value or the reason why the promise cannot is fulfilled.

Apart from genteel's interpretation, promise is an abstraction of a task. Promise's related APIs provide a set of methods and objects to implement this abstraction.

There are a number of implementations of promise:

    • ECMAScript Promise4
      • That is, the native Promise object, chrome32+ above support
    • Promise/a+5 Standard
      • kriskowal/q
      • Cujojs/when
      • Tildeio/rsvp.js
    • Other vendor standards
      • Jquery.deferred
      • WinJS

Although the standards are many, all implementations basically follow the following basic rules:

    • Promise Object
      • is a finite state machine
        • Complete (fulfilled)
        • Negation (rejected)
        • Wait (pending)
        • End (settled)
      • There must be a then([fulfill], [reject]) way for users to handle successful failures individually.
      • Optional done([fn]) , fail([fn]) method
      • Support Chain API
    • Deffered Object
      • Provide reject and resolve methods to complete a promise

The author will introduce the concrete mechanism and realization of promise in the special article. Just a little here, use the basic, anywhere jquery to solve the async problem in the small scene before you:

$("#sexyButton").on("click", function(data) {    $.getJSON("/api/topcis").done(function(data) {        var list = data.topics.map(function(t) {            return t.id + ". " + t.title + "/n";        });        var id = confirm("which topcis are you interested in? Select by ID : " + list);        $.getJSON("/api/topics/" + id).done(function(done) {            alert("Detail topic: " + data.content);        });    });});

Unfortunately, using promise does not make the callback problem much better. In this scenario, the promise does not reflect its strength. Let's take a look at the examples in the official jquery documentation:

$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) ).done(function( a1, a2 ) {  // a1 and a2 are arguments resolved for the page1 and page2 ajax requests, respectively.  // Each argument is an array with the following structure: [ data, statusText, jqXHR ]  var data = a1[ 0 ] + a2[ 0 ]; // a1[ 0 ] = "Whip", a2[ 0 ] = " It"  if ( /Whip It/.test( data ) ) {    alert( "We got what we came for!" );  }});

Here, two AJAX requests are initiated, and the two promise are merged into one, and the developer only deals with this final promise.

For Q.js example when.js , or a third-party library that can support more complex features. will also make your code style a great change. It can be said that promise opens up new doors for dealing with complex processes, but it also has a cost. These complex packages have a considerable overhead of 6.

Geneartor

ES6 's generator introduces an yield expression that makes process control more variable. Node-fiber lets us see what it coroutine looks like in JavaScript.

var Fiber = require(‘fibers‘);function sleep(ms) {    var fiber = Fiber.current;    setTimeout(function() {        fiber.run();    }, ms);    Fiber.yield();}Fiber(function() {    console.log(‘wait... ‘ + new Date);    sleep(1000);    console.log(‘ok... ‘ + new Date);}).run();console.log(‘back in main‘);

But imagine that if every JavaScript has this feature, then a variety of attempts by a normal JavaScript programmer will be challenged. Your object will be inexplicably changed by the code in another fiber.

In other words, there is no grammatical design that can be used to mix JavaScript code that supports fiber and does not support fiber and does not cause confusion. This non-portability of node-fiber makes coroutine less realistic in JavaScript 7.

But yield it is a shallow coroutines, it can only stop the user code, and only if it GeneratorFunction can be used yield .

In another article, I have explained in detail how to use Geneator to solve the problem of asynchronous process.

Using the yield implementation suspend approach, we can get a very brief introduction to our previous problem solving:

$("#sexyButton").on("click", function(data) {    suspend(function *() {        var data = yield $.getJSON("/api/topcis");        var list = data.topics.map(function(t) {            return t.id + ". " + t.title + "/n";        });        var id = confirm("which topcis are you interested in? Select by ID : " + list);        var detail = yield $.getJSON("/api/topics/");        alert("Detail topic: " + detail.content);    })();});

In order to use yield , we also have a choice:

    1. Generator compatibility is not good, only the new version of node and chrome support
    2. Requires a lot of rewriting of the underlying framework, which is interface normalization (thunkify), to support yield some of the constraints
    3. yieldThe resulting code style may confuse some novices
    4. Multilayer yield generated stacks and difficult to debug
Conclusion

With so much to say, asynchronous programming, which is quite different from the threading model, allows more programmers to understand the charms of the node as it pops up. If the next time C or Java programmer says that the JavaScript callback is too ugly, please let him read this article!

Original: http://hao.jser.com/archive/4296/

ASYNC programing in Javascript[turn]

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.