javascript異步編程方案彙總剖析

js-async-demos

目錄結構:
|—— 回調函數javascript

|—— 事件監聽css

|—— 發佈/訂閱html

|—— promise前端

|—— generator(es6生產函數)java

|—— async(es7異步函數)node

|—— 其餘jquery

|—— Rxjs(Observable對象)

此demo項目僅做爲我的對js整個異步編程發展的總結匯總,遂參考了多方資料,已在末尾備註git

asynchronous development process of javascript and some demoses6

回調函數callback

回調函數即函數的某個參數爲function,會掉函數在拿到上一步結果後執行,如:github

funa(param1, callback){
    ....
    callback(xxx);
}
▶︎
all
running...
 

事件監聽

採用事件驅動模式,任務的執行不取決代碼的順序,而取決於某一個事件是否發生。

監聽函數有: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')

▶︎
all
running...
 

這種方法的優勢:比較容易理解,能夠綁定多個事件,每個事件能夠指定多個回調函數,並且能夠去耦合,有利於實現模塊化。
這種方法的缺點:整個程序都要變成事件驅動型,運行流程會變得不清晰。
事件監聽方法:
(1)onclick方法

element.onclick=function(){
   //處理函數
}
▶︎
all
running...
 

優勢:寫法兼容到主流瀏覽器
缺點:當同一個element元素綁定多個事件時,只有最後一個事件會被添加,例如:

// 只有handler3會被添加執行,因此咱們使用另一種方法添加事件
element.onclick=handler1;
element.onclick=handler2;
element.onclick=handler3;
▶︎
all
running...
 

(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);

▶︎
all
running...
 

(三)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);
}
▶︎
all
running...
 

發佈/訂閱

咱們假定,存在一個」信號中心」,某個任務執行完成,就向信號中心」發佈」(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);
}
▶︎
all
running...
 

jQuery.publish("done")的意思是,f1執行完成後,向」信號中心」jQuery發佈」done」信號,從而引起f2的執行。
此外,f2完成執行後,也能夠取消訂閱(unsubscribe)。

jQuery.unsubscribe("done", f2);
▶︎
all
running...
 

這種方法的性質與」事件監聽」相似,可是明顯優於後者。由於咱們能夠經過查看」消息中心」,瞭解存在多少信號、每一個信號有多少訂閱者,從而監控程序的運行。

promise對象

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
▶︎
all
running...
 

可是,Promise的寫法只是回調函數的改進,使用then()以後,異步任務的兩段執行看得更清楚,除此以外並沒有新意。撇開優勢,Promise的最大問題就是代碼冗餘,原來的任務被Promise包裝一下,無論什麼操做,一眼看上去都是一堆then(),本來的語意變得很不清楚。

Generator(from es6)

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。

▶︎
all
running...
 

async函數

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);
});

▶︎
all
running...
 

async函數對 Generator 函數的改進,體如今如下四點。

  • 內置執行器

  • 更好的語義

  • 更廣的適用性

  • 返回值是Promise

async 函數的實現原理:
async 函數的實現原理,就是將 Generator 函數和自動執行器,包裝在一個函數裏

async function fn(args) {
    // ...
}
// 等同於
function fn(args) {
    return spawn(function* () {
       // ...
    });
}
▶︎
all
running...
 

其餘

RxJS

概述:Observable對象格式

參考文章&感謝

JS的四種異步方式::回調/監聽/流程控制庫/promise

前端基本知識(四):JS的異步模式:一、回調函數;二、事件監聽;三、觀察者模式;四、promise對象89

JavaScript異步編程的終極演變

阮一峯es6教程

相關文章
相關標籤/搜索