亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

詳談nodejs異步編程

2019-11-20 13:49:16
字體:
來源:轉載
供稿:網友

目前需求中涉及到大量的異步操作,實際的頁面越來越傾向于單頁面應用。以后可以會使用backbone、angular、knockout等框架,但是關于異步編程的問題是首先需要面對的問題。隨著node的興起,異步編程成為一個非常熱的話題。經過一段時間的學習和實踐,對異步編程的一些細節進行總結。

1.異步編程的分類

     解決異步問題方法大致包括:直接回調、pub/sub模式(事件模式)、異步庫控制庫(例如async、when)、promise、Generator等。
1.1 回調函數

      回調函數是常用的解決異步的方法,經常接觸和使用到,易于理解,并且在庫或函數中非常容易實現。這種也是大家接使用異步編程經常使用到的方法。

      但是回調函數的方式存在如下的問題:

     1. 可能形成萬惡的嵌套金字塔,代碼不易閱讀;

     2. 只能對應一個回調函數,在很多場景中成為一個限制。

1.2 pub/sub模式(事件)

     該模式也稱為事件模式,是回調函數的事件化,在jQuery等類庫中非常常見。

     事件發布訂閱者模式本身并無同步與異步調用的問題,但是在node中,emit調用多半是伴隨事件循環而異步觸發的。該模式常用來解耦業務邏輯,事件發布者無須關注注冊的回調函數,也不用關注回調函數的個數,數據通過消息的方式可以很靈活的傳遞。

     該模式的好處是:1. 便于理解;2. 不再局限于一個回調函數。

     不好的地方時:1. 需要借助類庫; 2.事件與回調函數的順序很重要

復制代碼 代碼如下:

var img = document.querySelect(#id);
img.addEventListener('load', function() {
  // 圖片加載完成
    ......
});
img.addEventListener('error', function() {
  // 出問題了
  ......
});

  上述代碼存在兩個問題:

      a. img實際已經加載完成,此時才綁定load回調函數,結果回調不會執行,但依然希望執行該對應回調函數。

復制代碼 代碼如下:

var img = document.querySelect(#id);
function load() {
  ...
}
if(img.complete) {
  load();
} else {
  img.addEventListener('load', load);
}
img.addEventListener('error', function() {
  // 出問題了
  ......
});

   b. 無法很好處理存在異常

      結論:事件機制最適合處理同一個對象上反復發生的事情,不需要考慮當綁定回調函數之前事件發生的情況。

1.3 異步控制庫

      目前的異步庫主要有Q、when.js、win.js、RSVP.js等。

      這些庫的特點是代碼是線性的,可以從上到下完成書寫,符合自然習慣。

      不好的地方也是風格各異,不便于閱讀,增加學習成本。

1.4 Promise

     Promise翻譯成中文為承諾,個人理解是異步完成之后,就會給外部一個結果(成功或失?。?,并承諾結果不再發生改變。換句話就是Promise反應了一個操作的最終返回結果值(A promise represents the eventual value returned from the single completion of an operation)。目前Promise已經引入到ES6規范里面,Chrome、firefox等高級瀏覽器已經在內部實現了該原生方法,使用起來相當方便。

     下面從如下幾個方面來解析Promise的特點:

    1.4.1 狀態

     包含三種狀態:pending、fulfilled、rejected,三種狀態只能發生兩種轉換(從pending--->fulfilled、pending―>rejected),并且狀態的轉換僅能發生一次。

    1.4.2 then方法

    then方法用于指定異步事件完成之后的回調函數。

   這個方法可以說是Promise的靈魂方法,該方法讓Promise充滿了魔力。有如下幾個具體表現:

    a) then方法返回Promise。這樣就實現了多個異步操作的串行操作。

     關于上圖中黃圈1的對value的處理是Promise里面較為復雜的一個地方,value的處理分為兩種情況:Promise對象、非Promise對象。

     當value 不是Promise類型時,直接將value作為第二個Promise的resolve的參數值即可;當為Promise類型時,promise2的狀態、參數完全由value決定,可以認為promsie2完全是value的傀儡,promise2僅僅是連接不同異步的橋梁。

復制代碼 代碼如下:

Promise.prototype.then = function(onFulfilled, onRejected) {
    return new Promise(function(resolve, reject) {           //此處的Promise標注為promise2
        handle({
            onFulfilled: onFulfilled,
            onRejected: onRejected,
            resolve: resolve,
            reject: reject
        })
    });
}
function handle(deferred) {
    var handleFn;
    if(state === 'fulfilled') {
        handleFn = deferred.onFulfilled;
    } else if(state === 'rejected') {
        handleFn = deferred.onRejected;
    }
    var ret = handleFn(value);
    deferred.resolve(ret);                           //注意,此時的resolve是promise2的resolve
}
function  resolve(val) {
    if(val && typeof val.then === 'function') {
        val.then(resolve);                           // if val為promise對象或類promise對象時,promise2的狀態完全由val決定
        return;
    }
    if(callback) {                                    // callback為指定的回調函數
        callback(val);
    }
}

  b)實現了多個不同異步庫之間的轉換。

    在異步中存在一個叫thenable的對象,就是指具有then方法的對象,只要一個對象對象具有then方法,就可以對其進行轉換,例如:

復制代碼 代碼如下:

var deferred = $('aa.ajax');      // !!deferred.then  === true
var P = Promise.resolve(deferred);
p.then(......)

1.4.3 commonJS Promise/A規范

      目前關于Promise的規范存在Promise/A和Promise/A+規范,這說明關于Promise的實現是挺復雜的。

復制代碼 代碼如下:

then(fulfilledHandler, rejectedHandler, progressHandler)

1.4.4 注意事項

     一個Promise里面的回調函數是共享value的,在結果處理中value作為參數傳遞給相應的回調函數,如果value是對象,那就要小心不要輕易修改value的值。

復制代碼 代碼如下:

var p = Promise.resolve({x: 1});
p.then(function(val) {
    console.log('first callback: ' + val.x++);
});
p.then(function(val) {
    console.log('second callback: ' + val.x)
})
// first callback: 1
// second callback: 2

1.5 Generator

      上面所有的方法均是基于回調函數來完成異步操作的,無非是對回調函數進行封裝而已。ES6里面提出了Generator,增加了解決異步操作的途徑,不再依據回調函數來完成。

      Generator最大的特點就是可以實現函數的暫停、重啟,這個特性非常有利于解決異步操作。將Generator的暫停與promise的異常處理結合起來,可以比較優雅地解決異步編程問題。具體實現參考:Kyle Simpson

2. 異步編程存在的問題

      2.1 異常處理

        a) 異步事件包括兩個環節:發出異步請求、結果處理,這兩個環節通過event loop來連接起來。那么try catch來進行異常捕獲的時候就需要分來捕獲。

復制代碼 代碼如下:

try {
    asyncEvent(callback);
} catch(err) {
    ......
}

     上述代碼是無法捕獲callback里面的異常,只能獲取發出請求環節的異常。這樣就存在問題:假如請求的發出和請求的處理是兩個人完成的,那么在異常處理的時候就存在問題?

        b)promise實現異常的傳遞,這帶來一些好處,在實際項目中保證代碼不被阻塞。但是如果異步事件比較多的時候,不容易找出到底是那個異步事件產生了異常。

復制代碼 代碼如下:

// 場景描述: 在CRM里面展示價格的報警信息,其中包含競對的信息。但是獲取競對的信息時間比較長,后端為了避免慢查詢,就把一條記錄拆成兩塊分別獲取。
// 第一步:獲取價格報警信息,除了競對信息
function getPriceAlarmData() {
    return new Promise(function(resolve) {
        Y.io(url, {
            method: 'get',
            data: params,
            on: function() {
                success: function(id, data) {
                    resolve(alarmData);
                }
            }
        });
    });
}
// 得到報警信息后,在去獲取競對信息
getPriceAlarmData().then(function(data) {
    // 數據渲染,除了競對信息
    render(data);
    return new Promise(function(resolve) {
        Y.io(url, {
            method: 'get',
            data: {alarmList: data},
            on: function() {
                success: function(id, compData) {
                    resolve(compData);
                }
            }
        });
    });
})      //  獲取完所有數據后進行競對信息的渲染
.then(function(data) {
    // 渲染競對信息
    render(data)
}, function(err) {
    // 異常處理
    console.log(err);
});

      可以把上述代碼轉換成如下:

復制代碼 代碼如下:

try{
    // 獲取除競對以外的報警信息
    var alarmData = alarmDataExceptCompare();
    render(alarmData);
    // 根據報警信息查詢競對信息
    var compareData = getCompareInfo(alarmData);
    render(compareData);
} catche(err) {
    console.log(err.message);
}

在上述例子中把異常處理放到最后進行處理,這樣當其中存在某個環節出現異常,我們無法準確知道到底是哪個事件產生的。     

2.2 jQuery.Deferred 的問題

     jQuery中也實現了異步操作,但是在實現上不符合promise/A+規范,主要表現在以下幾個方面:

    a. 參數的個數:標準的Promise只能接受一個參數,而jQuery中則可以傳遞多個參數

復制代碼 代碼如下:

function asyncInJQuery() {
    var d = new $.Deferred();
    setTimeout(function() {
        d.resolve(1, 2);
    }, 100);
    return d.promise()
}
asyncInJQuery().then(function(val1, val2) {
    console.log('output: ', val1, val2);
});
// output: 1 2

 b. 結果處理中異常的處理  

復制代碼 代碼如下:

function asyncInPromise() {
      return new Promise(function(resolve) {
        setTimeout(function() {
          var jsonStr = '{"name": "mt}';
          resolve(jsonStr);
        }, 100);
      });
  }
  asyncInPromise().then(function(val) {
      var d = JSON.parse(val);
      console.log(d.name);
  }).then(null, function(err) {
    console.log('show error: ' + err.message);
  });
// show error: Unexpected end of input
function asyncInJQuery() {
    var d = new $.Deferred();
    setTimeout(function() {
        var jsonStr = '{"name": "mt}';
        d.resolve(jsonStr);
    }, 100);
    return d.promise()
}
asyncInJQuery().then(function(val) {
    var d = JSON.parse(val);
    console.log(d.name);
}).then(function(v) {
    console.log('success: ', v.name);
}, function(err){
    console.log('show error: ' + err.message);
});
//Uncaught SyntaxError: Unexpected end of input

     從中可以看出,Promise對回調函數進行了結果處理,可以捕獲回調函數執行過程中的異常,而jQuery.Deferred卻不可以。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲视频第一页| 亚洲女人天堂网| 国产精品永久免费在线| 精品国产乱码久久久久久婷婷| 亚洲国产精品99久久| 丁香五六月婷婷久久激情| 日韩欧美中文免费| 日本高清久久天堂| 欧美日韩在线免费| 久久久久亚洲精品| 国产成人97精品免费看片| 久久亚洲精品毛片| 亚洲国产精品悠悠久久琪琪| 欧美日韩另类视频| 国产精品免费久久久久久| 自拍偷拍亚洲区| 国产精品日韩精品| 国内精品视频一区| 国产99久久精品一区二区| 97色伦亚洲国产| 成人久久一区二区| 亚洲精选在线观看| 国产欧美久久久久久| 日韩国产激情在线| 日本国产一区二区三区| 日韩欧美中文免费| 国产91精品不卡视频| 日韩av最新在线观看| 亚洲欧洲黄色网| 另类专区欧美制服同性| 久久精品美女视频网站| 亚洲男人天堂2023| 欧洲永久精品大片ww免费漫画| 日韩国产欧美精品一区二区三区| 久久久久免费视频| 日韩精品在线影院| 国产mv久久久| 中文字幕日韩在线视频| 日韩成人在线网站| 国产99久久精品一区二区永久免费| 亚洲一二在线观看| 亚洲国产私拍精品国模在线观看| 国产欧美精品在线播放| 久久99视频精品| 日韩免费视频在线观看| 日韩极品精品视频免费观看| 欧美精品一二区| 亚洲一区免费网站| 日本久久精品视频| 欧美激情精品久久久久久黑人| 欧洲成人免费aa| 亚洲va电影大全| 在线日韩日本国产亚洲| 亚洲a成v人在线观看| 91av在线免费观看视频| 欧美在线免费看| 91免费在线视频网站| 欧美精品videofree1080p| 91免费综合在线| 91影院在线免费观看视频| 欧美日韩精品在线| 欧美午夜精品久久久久久人妖| 九九热r在线视频精品| 亚洲精品中文字幕女同| 久久999免费视频| 亚洲激情免费观看| 国产精品黄视频| 日韩欧美在线字幕| 国产精品高清网站| 欧美精品www在线观看| 日韩国产一区三区| 热久久免费国产视频| 国产精品久久久久久久久免费看| 亚洲精品午夜精品| 欧美激情视频在线| 亚洲国产成人久久综合| 亚洲国产精品久久久久秋霞蜜臀| 欧美日韩一区二区三区在线免费观看| 欧美有码在线视频| 日韩中文字在线| 亚洲欧美精品中文字幕在线| 久久香蕉国产线看观看网| 成人美女av在线直播| 久久精品精品电影网| 97av在线播放| 精品久久香蕉国产线看观看亚洲| 精品国产视频在线| 一本一道久久a久久精品逆3p| 91在线视频免费| www日韩中文字幕在线看| 国产美女被下药99| 日韩精品在线免费观看视频| 国产精品∨欧美精品v日韩精品| 欧美亚州一区二区三区| 中文字幕精品久久| 川上优av一区二区线观看| 国产精品久久久久久久9999| 中文字幕精品视频| 68精品国产免费久久久久久婷婷| 久久国产精品视频| 亚洲一品av免费观看| 精品精品国产国产自在线| 欧美丰满片xxx777| 精品一区二区三区四区| 欧美资源在线观看| www.日本久久久久com.| 国产日韩欧美日韩大片| 久久久成人av| 亚洲福利视频久久| 91久久精品国产91久久性色| 久久久免费电影| 在线观看日韩欧美| 久久乐国产精品| 亚洲男女自偷自拍图片另类| 黑人与娇小精品av专区| 成人综合网网址| 亚洲欧美激情视频| 激情av一区二区| 777精品视频| 国产一区二区三区毛片| 97人洗澡人人免费公开视频碰碰碰| 成人在线精品视频| 亚洲精品国产综合久久| 欧美日韩亚洲91| 久久久噜噜噜久久久| 日本亚洲欧洲色| 91情侣偷在线精品国产| 国产精品视频免费观看www| 91超碰中文字幕久久精品| 91久久嫩草影院一区二区| 亚洲一区二区三区视频| 91精品国产综合久久久久久久久| 91精品视频大全| 亚洲精品自拍第一页| 久久亚洲综合国产精品99麻豆精品福利| 久久久综合av| 亚洲欧美在线一区| 欧美日韩激情美女| 久久噜噜噜精品国产亚洲综合| 国产精品视频免费在线| 亚洲欧美三级伦理| 成人在线激情视频| 欧美一区二区三区图| 久久全国免费视频| 国产精品看片资源| 97超视频免费观看| 热门国产精品亚洲第一区在线| 精品亚洲aⅴ在线观看| 国产在线播放不卡| 久久九九精品99国产精品| 国产免费一区二区三区在线能观看| 日本精品性网站在线观看| 国产精品久久二区| 国产美女高潮久久白浆| 欧美日韩免费在线| 欧美精品亚州精品| 日韩欧美福利视频| 91av在线免费观看视频| 欧美日韩国产成人在线| 欧美丝袜一区二区三区| 国产精品6699| 国产精品日韩专区| 欧美精品久久久久久久久| 精品久久久久久国产|