目錄結構:
|—— 回調函數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