在我们日常编码中,需要异步的场景很多,比如读取文件内容、获取远程数据、发送数据到服务端等。因为浏览器环境里Javascript是单线程的,所以异步编程在前端领域尤为重要。

异步的概念

所谓异步,是指当一个过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的过程在完成后,通过状态、通知或者回调来通知调用者。

比如我们写这篇文字时点击发布按钮,我们并不能马上得到文章发布成功或者失败。等待服务器处理,这段时间我们可以做其他的事情,当服务器处理完成后,通知我们是否发布成功。

所谓同步,是指当一个过程调用发出后,必须等待这个过程处理完成后,再处理其他事情。即堵塞执行。

异步的方式

es6之前,我们实现异步有4种方法,回调、事件、发布订阅和promise方式。

异步之回调:

function dealTask(param, callback) {
// Deal with some time consuming tasks.
// ...
Object.prototype.toString.call(callback) === '[object Function]' ? callback() : null;
} dealTask({ id: 1 }, function() {
console.log('... I am in the callback...');
})

回调的方式来实现异步其实就是把需要在当前任务完成后执行的函数当成参数传入,完成任务后执行即可。

异步之事件

function dealTask(param) {
// Deal with some time consuming tasks.
// ...
events.trigger('dealTaskFinish')
}
events.on('dealTaskFinish', function() {
console.log('...I am in the end...');
})

通过事件来实现回调,好处是方便实用,跨模块传递数据。坏处是,事件用的多了后业务逻辑混乱,不知道哪里注册过哪里监听过。

另外需要注意的是在web component场景下,mount后注册过的事件需要在unmount释放,不然会导致内存泄露。

异步之发布订阅

发布订阅的简单例子是,一个开关,同时并联几个灯泡(在不同房间),触发的时候,几个灯泡都会得到指令,然后执行发光的行为。

// 使用pubsubz实现
var testSubscriber = function(data ){
console.log(data );
};
var testSubscription = pubsubz.subscribe( 'example', testSubscriber );
pubsubz.publish( 'example', 'hello' );

订阅发布与的性质与"事件监听"类似,不同的是,我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

异步之Promise

function helloWorld (ready) {
return new Promise(function (resolve, reject) {
if (ready) {
resolve("Hello World!")
} else {
reject("Good bye!")
}
})
} helloWorld(true).then(function (message) {
console.log(message)
}, function (error) {
console.log(error)
})

Promises对象是CommonJS工作组提出的一种规范,是对异步编程的一种统一,其实也就是语法糖,可阅读性变强了而已。


在ES6出来以后,我们的异步方式也发生了改变。

异步之Generator

Generator函数是协程在ES6的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

Generator函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。

function* Foo(x) {
yield x + 1; var y = yield null;
return x + y;
} var foo = Foo(5);
foo.next(); // { value: 6, done: false }
foo.next(); // { value: null, done: false }
foo.next(8); // { value: 13, done: true }

next方法返回值的value属性,是Generator函数向外输出数据;next方法还可以接受参数,这是向Generator函数体内输入数据。

yield命令用于将程序的执行权移出Generator函数,那么就需要一种方法,将执行权再交还给Generator函数

上面的方式,是我们手动调用Generator函数执行,但是当我们的需要执行next方法很多时,就需要Generator函数自动执行了。

Generator函数自动执行的意思是,通过一定的方法来自动执行next方法,比如:

function autoRunGen(gen){
var g = gen(); function next(data){
var result = g.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}

co模块是TJ开发的一个小工具,用于Generator函数的自动执行。他的主要思想和上面的代码片段类似。

使用co的前提条件是,Generator函数的yield命令后面,只能是Promise对象。

co(function *() {
var data = yield $.get('/api/data');
console.log(data);
var user = yield $.get('/api/user');
console.log(user);
var products = yield $.get('/api/products');
console.log(products);
});

co模块使得我们可以像写同步代码一样,写异步代码。

异步之async/await

async函数仅仅是Generator函数的语法糖。

var fs = require('fs');

var readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) reject(error);
resolve(data);
});
});
}; var gen = function* (){
var f1 = yield readFile('/etc/a.js');
var f2 = yield readFile('/etc/b.js');
console.log(f1.toString());
console.log(f2.toString());
};

使用async/await方式:

var asyncReadFile = async function (){
var f1 = await readFile('/etc/a.js');
var f2 = await readFile('/etc/b.js');
console.log(f1.toString());
console.log(f2.toString());
};

async函数就是将Generator函数的星号(*)替换成async,将yield替换成await,仅此而已。

不同的是,Generator执行需要手动执行,而async函数可以自动执行,像写同步一样写异步。Generator返回Iterator对象,async函数返回Promise对象。

探索Javascript 异步编程的更多相关文章

  1. 探索Javascript异步编程

    异步编程带来的问题在客户端Javascript中并不明显,但随着服务器端Javascript越来越广的被使用,大量的异步IO操作使得该问题变得明显.许多不同的方法都可以解决这个问题,本文讨论了一些方法 ...

  2. JavaScript异步编程(2)- 先驱者:jsDeferred

    JavaScript当前有众多实现异步编程的方式,最为耀眼的就是ECMAScript 6规范中的Promise对象,它来自于CommonJS小组的努力:Promise/A+规范. 研究javascri ...

  3. JavaScript异步编程的主要解决方案—对不起,我和你不在同一个频率上

    众所周知(这也忒夸张了吧?),Javascript通过事件驱动机制,在单线程模型下,以异步的形式来实现非阻塞的IO操作.这种模式使得JavaScript在处理事务时非常高效,但这带来了很多问题,比如异 ...

  4. JavaScript异步编程原理

    众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...

  5. javascript异步编程的前世今生,从onclick到await/async

    javascript与异步编程 为了避免资源管理等复杂性的问题, javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是为 ...

  6. Promises与Javascript异步编程

    Promises与Javascript异步编程 转载:http://www.zawaliang.com/2013/08/399.html 在如今都追求用户体验的时代,Ajax应用真的是无所不在.加上这 ...

  7. 5分种让你了解javascript异步编程的前世今生,从onclick到await/async

      javascript与异步编程 为了避免资源管理等复杂性的问题,javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是 ...

  8. 转: Promises与Javascript异步编程

    在如今都追求用户体验的时代,Ajax应用真的是无所不在.加上这些年浏览器技术.HTML5以及CSS3等的发展,越来越多的富Web应用出现:在给与我们良好体验的同时,Web开发人员在背后需要处理越来越多 ...

  9. JavaScript异步编程

    前言 如果你有志于成为一个优秀的前端工程师,或是想要深入学习JavaScript,异步编程是必不可少的一个知识点,这也是区分初级,中级或高级前端的依据之一.如果你对异步编程没有太清晰的概念,那么我建议 ...

随机推荐

  1. numpy.linspace介绍

    numpy.linspace:在指定范围内返回均匀间隔的数组 In [12]: import numpy as np In [13]: result = np.linspace(1,10) #默认生成 ...

  2. Django之查询总结

    models.Book.objects.filter(**kwargs): querySet [obj1,obj2]models.Book.objects.filter(**kwargs).value ...

  3. Mysql数据表字段设置了默认值,插入数据后默认字段的值却为null,不是默认值

    我将mysql的数据表的某个字段设置了默认值为1,当向该表插入数据的时候该字段的值不是默认值,而是null. 我的错误原因: 对数据库的操作我使用了持久化工具mybatis,插入数据的时候插入的是整个 ...

  4. maven的相关命令

    maven的相关命令 mvn archetype:create :创建 Maven 项目 mvn compile :编译源代码(编译到target文件夹中) mvn test-compile :编译测 ...

  5. CentOS中nginx负载均衡和反向代理的搭建

    1: 修改centos命令行启动(减少内存占用): vim /etc/inittab :initdefault: --> 修改5为3 若要界面启动使用 startx 2:安装jdk )解压:jd ...

  6. dbml 注意事项

    1,修改dbml中的字段,需要修改2个地方

  7. overflow应用场景

    overflow属性可以设置的值有5种: (1)visible  默认值,内容不会裁剪,呈现在元素框之外: (2)hidden 内容会被裁剪,并且子元素内容是不可见的: (3)scroll 内容会被裁 ...

  8. 8月份的To-Do List

    1.汲取归纳<Effective Objective-C 2.0 >的知识点 2.回顾网易云课堂翁恺老师的C语言相关课程, 为学习算法做好准备 3.读完Kelly McGonigal的&l ...

  9. 一个线程知识点, 一个MongoDB的知识点

    //WINForm窗体中切换前后台线程执行任务: protected void RunOnUI(Action action) { Invoke(action); } protected void Ru ...

  10. MySql判断汉字、日期、数字的具体函数

    几个平常用的mysql函数,MySql判断汉字.日期.数字的具体函数分享给大家,具体内容如下 1.判断字符串是否为汉字 返回值:1-汉字 0-非汉字 ? 1 2 3 4 5 6 7 8 9 10 11 ...