惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Jina AI
Jina AI
美团技术团队
博客园 - 聂微东
博客园 - 叶小钗
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园_首页
Spread Privacy
Spread Privacy
J
Java Code Geeks
雷峰网
雷峰网
宝玉的分享
宝玉的分享
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Privacy International News Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
The Hacker News
The Hacker News
量子位
L
LINUX DO - 热门话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
G
GRAHAM CLULEY
D
Darknet – Hacking Tools, Hacker News & Cyber Security
月光博客
月光博客
腾讯CDC
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tor Project blog
罗磊的独立博客
V
Vulnerabilities – Threatpost
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
Project Zero
Project Zero
Hugging Face - Blog
Hugging Face - Blog
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tenable Blog
MyScale Blog
MyScale Blog
T
The Exploit Database - CXSecurity.com
GbyAI
GbyAI
博客园 - 【当耐特】
O
OpenAI News
Schneier on Security
Schneier on Security
S
Secure Thoughts
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Securelist
博客园 - 司徒正美

Minko Gechev's blog

skillgrade Unit Tests for AI Agent Skills You Should Care About AI Generative Development LLM-first Web Framework Reactive framework in ~200 lines of JavaScript Managing Angular Are LLMs going to replace us? Prefetching Heuristics Design Patterns in Open Source Projects - Part II Design Patterns in Open Source Projects - Part I What I learned doing 125 public talks - Part I Dynamic imports solve all the problems, right? 5 Angular CLI Features You Didn't Know About Angular quicklink Preloading Strategy Introducing Bazel Schematics for Angular CLI Building TypeScript Projects with Bazel Joining Google Playing Mortal Kombat with TensorFlow.js. Transfer learning and data augmentation Fast, extensible, configurable, and beautiful linter for Go Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web Machine Learning-Driven Bundling. The Future of JavaScript Tooling. JavaScript Decorators for Declarative and Readable Code 3 Tricks For Using Redux and Immutable.js with TypeScript Follow Your Dream Career with Open Source. Personal Story. Redux Anti-Patterns - Part 1. State Management. Faster Angular Applications - Understanding Differs. Developing a Custom IterableDiffer Faster Angular Applications - Part 2. Pure Pipes, Pure Functions and Memoization Faster Angular Applications - Part 1. On Push Change Detection and Immutability Understanding Dynamic Scoping and TemplateRef Implementing a Simple Compiler on 25 Lines of JavaScript Developing Statically Typed Programming Language WebVR for a Gamified IDE 7 Angular Tools That You Should Consider Announcing ngrev - Reverse Engineering Tool for Angular Implementing Angular's Dependency Injection in React. Understanding Element Injectors. Distributing an Angular Library - The Brief Guide Angular in Production Ahead-of-Time Compilation in Angular 2.5X Smaller Angular 2 Applications with Google Closure Compiler Using Stripe with Angular (Deprecated) Building an Angular Application for Production Implementing the Missing "resolve" Feature of the Angular 2 Router Scalable Single-Page Application Architecture Managing ambient type definitions and dealing with the "Duplicate identifier" TypeScript error Static Code Analysis of Angular 2 and TypeScript Projects Enforcing Best Practices with Static Code Analysis of Angular 2 Projects ViewChildren and ContentChildren in Angular Dynamically Configuring the Angular's Router Angular 2 Hot Loader Lazy Loading of Route Components in Angular 2 Aspect-Oriented Programming in JavaScript Flux in Depth. Store and Network Communication. Using JSX with TypeScript Flux in Depth. Overview and Components. Even Faster AngularJS Data Structures Boost the Performance of an AngularJS Application Using Immutable Data - Part 2 Angular2 - First Impressions Build Your own Simplified AngularJS in 200 Lines of JavaScript Persistent State of ReactJS Component Boost the Performance of an AngularJS Application Using Immutable Data Processing Binary Protocols with Client-Side JavaScript Stream your Desktop to HTML5 Video Element Multi-User Video Conference with WebRTC Binary Tree iterator with ES6 generators WebRTC chat with React.js AngularJS in Patterns (Part 3) AngularJS in Patterns (Part 2). Services. Using GitHub Pages with Jekyll! AngularJS in Patterns (Part 1). Overview of AngularJS Singleton in JavaScript Express over HTTPS What I get from the JavaScript MV* frameworks Remote Desktop Client with AngularJS and Yeoman The magic of $resource (or simply a client-side Active Record) AngularJS Inheritance Patterns AngularAOP v0.1.0 Advanced JavaScript at Sofia University AngularJS style guide Lazy prefetching of AngularJS partials VNC client on 200 lines of JavaScript Aspect-Oriented Programming with AngularJS CSS3 flipping effect Practical programming with JavaScript Why I should use publish/subscribe in JavaScript JavaScript, the weird parts Functional programming with JavaScript plainvm Looking for performance? Probably you should NOT use [].sort (V8) JavaScript image scaling ELang Caching CSS with localStorage Self-invoking functions in JavaScript (or Immediately Invoked Function Expressions) Asus N56VZ + Ubuntu 12.04 (en) Asus N56VZ + Ubuntu 12.04 Debian Squeeze + LXDE on Google Nexus S (or having some fun while suffering) HTML5 image editor Курсови проекти – ФМИ Carousel Gallery SofiaJS...
Asynchronous calls with ES6 generators
2014-12-21 · via Minko Gechev's blog

ES6 generators have landed in Chrome 39 Beta on 9th of October and are already available in the stable version. This means that brand new development practices are already applicable, although for supporting older browsers you might need to fallback to the traceroute compiler.

A cool module, which TJ developed, called co, is available for quite a while (his initial commit in the co repository was on 6th of June 2013).

A few months ago Jake Archibald wrote an article about the awesomeness of the async functions in ES7 and how we can “emulate” them using ES6’s generators, using his spawn function, which has subset of the features of co.

In this blog post we’ll take a look at standard ways of handling asynchronous calls and how we can improve the readability of our code using generators. We’ll also take a further look at the implementation of spawn, since there are few tricky moments there.

The code from the demo is at my GitHub.

Prerequisites

You must be familiar with how JavaScript promises work. You can read this amazing HTML5 rocks article about them and take a look at my Light Q implementation for the underlaying details.

You should be also familiar with how generators work. For some initial, quick background I can recommend you “Binary Tree iterator with ES6 generators”.

Getting started

In this section we’re going to take a look at simple example, which handles asynchronous function calls with callbacks, promises and generators (we can think them as semi-coroutines).

We’ll use the most common example for this - AJAX calls.

Sample data set

Let’s suppose we have the following data, which should be resolved using XHR (XMLHttpRequests):

users.json

{
  "group": "admins",
  "users_list": ["user1.json", "user2.json"]
}

Where in each JSON file, corresponding to given user we have:

userx.json

{
  "username": "foobar",
  "avatar": "photox.png"
}

All we want to do is get the list of users with GET XHR, log the group name and after that get the users one by one (or at once if possible).

Callback solution

Let’s implement the getJSON method:

function getJSON(url, success, error) {
  'use strict';
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        success(JSON.parse(xhr.responseText));
      } else {
        error(xhr.responseText);
      }
    }
  };
  xhr.open('GET', url);
  xhr.send();
}

Our loadUsers function would look like this:

function loadUsers() {
  getJSON('users.json', function (users) {
    console.log(users.group);
    users.users_list.forEach(function (u) {
      getJSON(u, function (user) {
        console.log(user);
      }, function (error) {
        console.error(error);
      });
    });
  }, function (error) {
    console.error(error);
  });
}

This looks…alright, I guess. What we do is to fetch the users.json and later, in the success callback, fetch each user in the users_list array. Excluding the ugliness of the code (plenty of nested callbacks), the functionality is not that bad because we can fetch up-to 6 users simultaneously (that’s the limit in Chrome).

Promises solution

We can modify slightly getJSON in order to make it return a promise:

function getJSON(url) {
  'use strict';
  var xhr = new XMLHttpRequest();
  return new Promise((resolve, reject) => {
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        } else {
          reject(xhr.responseText);
        }
      }
    };
    xhr.open('GET', url);
    xhr.send();
  });
}

Now loadUsers will look like this:

function loadUsers() {
  getJSON('users.json')
  .then(function (data) {
    console.log(data.group);
    Promise.all(data.users_list.map(getJSON))
    .then(function (users) {
      console.log(users);
    });
  });
}

I believe, the code looks a little bit more compact. We still fetch up-to 6 users simultaneously, because of the call data.users_list.map(getJSON).

Solution with generators

Since async functions are still not implemented in the modern browsers, as Jake suggests, we can use generators as something akin to them. Let’s leave our getJSON implementation untouched and implement loadUsers using a generator:

function loadUsers() {
  spawn(function * () {
    var users = yield getJSON('users.json');
    for (var user of users.users_list.map(getJSON)) {
      console.log(yield user);
    }
  });
}

Looks much more elegant, doesn’t it? Now lets trace what is actually going on under the hood. Let’s try to implement spawn.

First of all, we should be aware of the fact that it accepts a generator function, so we should use it like:

function spawn(genFunc) {
  var generator = genFunc();
  // ...
}

After the first invocation of our generator we will receive a promise:

function spawn(genFunc) {
  var generator = genFunc();
  generator.next().value
  .then(function (users) {
    //...
  });
}

This snippet will invoke getJSON and will receive the returned promise (as generator.next().value). On resolve of the promise, the callback will be invoked with JavaScript object, equals to the parsed JSON we just fetched from users.json.

As next step we need to invoke the generator with the received object, this way the local variable users (inside loadUsers) will hold the correct value and we will have access to the users_list property, over which we need to iterate with map:

function spawn(genFunc) {
  var generator = genFunc();
  generator.next().value
  .then(function (users) {
    generator.next(users).value
    .then(function (user1) {
      //...
    })
  });
}

Once we invoke the generator with the users object we continue the execution of the generator and enter the loop (for...of) where we invoke yield with the first promise. Once the promise is resolved we enter the then callback and we can request the second user:

function spawn(genFunc) {
  var generator = genFunc();
  generator.next().value
  .then(function (users) {
    generator.next(users).value
    .then(function (user1) {
      generator.next(users).value
      .then(function (user2) {
        // Done!
      });
    })
  });
}

As you see we have a series of nested promise calls. We can generalize them into the following implementation of spawn:

function spawn(genFunc) {
  var generator = genFunc();
  function co(type, arg) {
    var res;
    try {
      res = generator[type](arg);
    } catch (e) {
      return Promise.reject(e);
    }
    if (res.done) {
      if (type === 'throw') {
        return arg;
      } else {
        return res.value;
      }
    } else {
      return Promise.resolve(res.value)
      .then(function (val) {
        return co('next', val);
      }, function (err) {
        return co('throw', err);
      });
    }
  }
  co('next');
}

Promise.resolve has the same functionality as Q.when, everything else is quite straightforward.

Note that the following code fetches all users one by one in contrast to the callback and the pure promise solution, where we fetch the users simultaneously.