目录结构:
|—— 回调函数javascript
|—— 事件监听css
|—— 发布/订阅html
|—— promise前端
|—— generator(es6生产函数)java
|—— async(es7异步函数)node
|—— 其余jquery
|—— Rxjs(Observable对象)
此demo项目仅做为我的对js整个异步编程发展的总结汇总,遂参考了多方资料,已在末尾备注git
asynchronous development process of javascript and some demoses6
回调函数即函数的某个参数为function,会掉函数在拿到上一步结果后执行,如:github
funa(param1, callback){ .... callback(xxx); }
采用事件驱动模式,任务的执行不取决代码的顺序,而取决于某一个事件是否发生。
监听函数有:on,bind,listen,addEventListener,observe
// 为f1绑定一个事件(采用jquery写法)。当f1发生done事件,就执行f2。 f1.on('done',f2); // 对f1进行改写, 执行完成后,当即触发done事件,从而开始执行f2. function f1(){ settimeout(function(){ //f1的任务代码 f1.trigger('done'); },1000); } f1.trigger('done')
这种方法的优势:比较容易理解,能够绑定多个事件,每个事件能够指定多个回调函数,并且能够去耦合,有利于实现模块化。
这种方法的缺点:整个程序都要变成事件驱动型,运行流程会变得不清晰。
事件监听方法:
(1)onclick方法
element.onclick=function(){ //处理函数 }
优势:写法兼容到主流浏览器
缺点:当同一个element元素绑定多个事件时,只有最后一个事件会被添加,例如:
// 只有handler3会被添加执行,因此咱们使用另一种方法添加事件 element.onclick=handler1; element.onclick=handler2; element.onclick=handler3;
(2)attachEvent和addEvenListener方法
// IE:attachEvent 三个方法执行顺序:3-2-1 elment.attachEvent("onclick",handler1); elment.attachEvent("onclick",handler2); elment.attachEvent("onclick",handler3); //标准addEventListener 执行顺序:1-2-3; elment.addEvenListener("click",handler1,false); elment.addEvenListener("click",handler2,false); elment.addEvenListener("click",handler3,false);
(三)DOM方法addEventListener()和removeListenner()
addEventListenner()和removeListenner()表示用来分配和删除事件的函数。这两种方法都须要三种参数,分别为:string(事件名称),要触发事件的函数function,指定事件的处理函数的时期或者阶段(boolean),例子见(二).
(四)通用的事件添加方法:
on:function(elment,type,handler){ //添加事件 return element.attachEvent?elment.attachEvent("on"+type,handler):elment.addEventListener(type,handler,false); }
咱们假定,存在一个”信号中心”,某个任务执行完成,就向信号中心”发布”(publish)一个信号,其余任务能够向信号中心”订阅”(subscribe)这个信号,从而知道何时本身能够开始执行。这就叫作”发布/订阅模式“(publish-subscribe pattern),又称”观察者模式“(observer pattern)。
这个模式有多种实现,下面采用的是Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件。
首先,f2向”信号中心”jQuery订阅”done”信号。
jQuery.subscribe("done", f2); //而后,f1进行以下改写: function f1(){ setTimeout(function () { // f1的任务代码 jQuery.publish("done"); }, 1000); }
jQuery.publish("done")的意思是,f1执行完成后,向”信号中心”jQuery发布”done”信号,从而引起f2的执行。
此外,f2完成执行后,也能够取消订阅(unsubscribe)。
jQuery.unsubscribe("done", f2);
这种方法的性质与”事件监听”相似,可是明显优于后者。由于咱们能够经过查看”消息中心”,了解存在多少信号、每一个信号有多少订阅者,从而监控程序的运行。
Promise的概念并非ES6新出的,而是ES6整合了一套新的写法。一样继续上面的例子,使用Promise代码就变成这样了:
/* promise的api方法: * promise construct * then * resolve/reject * catch * all * race: 顾名思义,Promse.race就是赛跑的意思,意思就是Promise.race([p1, p2, p3])里面哪一个结果得到的快就返回那个结果 * finally: */ var readFile = require('fs-readfile-promise'); readFile(fileA) .then((data)=>{console.log(data)}) .then(()=>{return readFile(fileB)}) .then((data)=>{console.log(data)}) // ... 读取n次 .catch((err)=>{console.log(err)}) // 注意:上面代码使用了Node封装好的Promise版本的readFile函数,它的原理其实就是返回一个Promise对象,咱也简单地写一个: var fs = require('fs'); var readFile = function(path) { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) reject(err) resolve(data) }) }) } module.export = readFile
可是,Promise的写法只是回调函数的改进,使用then()以后,异步任务的两段执行看得更清楚,除此以外并没有新意。撇开优势,Promise的最大问题就是代码冗余,原来的任务被Promise包装一下,无论什么操做,一眼看上去都是一堆then(),本来的语意变得很不清楚。
Generator(生成器)函数是ES6提供的一种异步编程解决方案,语法行为与传统函数彻底不一样。ES6将JavaScript异步编程带入了一个全新的阶段。
Generator 函数的暂停执行的效果,意味着能够把异步操做写在yield表达式里面,等到调用next方法时再日后执行。这实际上等同于不须要写回调函数了,由于异步操做的后续操做能够放在yield表达式下面
generator函数的特性以下,后面两个特性使它能够做为异步编程的完整解决方案:
暂停执行
恢复执行
函数体内外的数据交换
错误处理机制
// Ajax是典型的异步操做,经过Generator函数部署Ajax操做,能够用同步的方式表达。 function* main() { var result = yield request("http://some.url"); var resp = JSON.parse(result); console.log(resp.value); } function request(url) { makeAjaxCall(url, function(response){ it.next(response); }); } var it = main(); it.next(); // 上面代码的main函数,就是经过 Ajax 操做获取数据。能够看到,除了多了一个yield,它几乎与同步操做的写法彻底同样。注意,makeAjaxCall函数中的next方法,必须加上response参数,由于yield表达式,自己是没有值的,老是等于undefined。
ES2017标准引入了async函数,使得异步操做变得更加方便。但它实际上是是Generator函数的语法糖
// demo1: 一个 Generator 函数,依次读取两个文件。 const fs = require('fs'); const readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error) return reject(error); resolve(data); }); }); }; const gen = function* () { const f1 = yield readFile('/etc/fstab'); const f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; // 写成async函数,就是下面这样。 // 比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。 const asyncReadFile = async function () { const f1 = await readFile('/etc/fstab'); const f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; // demo2: async function getStockPriceByName(name) { const symbol = await getStockSymbol(name); const stockPrice = await getStockPrice(symbol); return stockPrice; } getStockPriceByName('goog').then(function (result) { console.log(result); });
async函数对 Generator 函数的改进,体如今如下四点。
内置执行器
更好的语义
更广的适用性
返回值是Promise
async 函数的实现原理:
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里
async function fn(args) { // ... } // 等同于 function fn(args) { return spawn(function* () { // ... }); }
概述:Observable对象格式
JS的四种异步方式::回调/监听/流程控制库/promise