搜尋
首頁web前端js教程詳談nodejs非同步程式設計_node.js

目前需求涉及大量的非同步操作,實際的頁面越來越傾向於單一頁面應用程式。以後可以使用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
        句柄({
            onFulfilled: onFulfilled,
            onRejected: onRejected,
            解:解,
            拒絕:拒絕
        })
    });
}
函數句柄(延遲){
    varhandleFn;
    if(state === '完成') {
        handleFn = deferred.onFulfilled;
    } else if(state === '拒絕') {
        handleFn = deferred.onRejected;
    }
    var ret = handleFn(value);
    deferred.resolve(ret);                           }
函數解析(val){
    if(val && typeof val.then === '函數') {
        val.then(解決);                      地         返回;
    }
    if(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(函數(val) {
    console.log('第一次回呼:' val.x );
});
p.then(函數(val) {
    console.log('第二次回呼:' val.x)
})
// 第一個回呼:1
// 第二個回呼: 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卻不可以。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Vercel是什么?怎么部署Node服务?Vercel是什么?怎么部署Node服务?May 07, 2022 pm 09:34 PM

Vercel是什么?本篇文章带大家了解一下Vercel,并介绍一下在Vercel中部署 Node 服务的方法,希望对大家有所帮助!

node.js gm是什么node.js gm是什么Jul 12, 2022 pm 06:28 PM

gm是基于node.js的图片处理插件,它封装了图片处理工具GraphicsMagick(GM)和ImageMagick(IM),可使用spawn的方式调用。gm插件不是node默认安装的,需执行“npm install gm -S”进行安装才可使用。

火了!新的JavaScript运行时:Bun,性能完爆Node火了!新的JavaScript运行时:Bun,性能完爆NodeJul 15, 2022 pm 02:03 PM

今天跟大家介绍一个最新开源的 javaScript 运行时:Bun.js。比 Node.js 快三倍,新 JavaScript 运行时 Bun 火了!

nodejs中lts是什么意思nodejs中lts是什么意思Jun 29, 2022 pm 03:30 PM

在nodejs中,lts是长期支持的意思,是“Long Time Support”的缩写;Node有奇数版本和偶数版本两条发布流程线,当一个奇数版本发布后,最近的一个偶数版本会立即进入LTS维护计划,一直持续18个月,在之后会有12个月的延长维护期,lts期间可以支持“bug fix”变更。

聊聊Node.js中的多进程和多线程聊聊Node.js中的多进程和多线程Jul 25, 2022 pm 07:45 PM

大家都知道 Node.js 是单线程的,却不知它也提供了多进(线)程模块来加速处理一些特殊任务,本文便带领大家了解下 Node.js 的多进(线)程,希望对大家有所帮助!

node爬取数据实例:聊聊怎么抓取小说章节node爬取数据实例:聊聊怎么抓取小说章节May 02, 2022 am 10:00 AM

node怎么爬取数据?下面本篇文章给大家分享一个node爬虫实例,聊聊利用node抓取小说章节的方法,希望对大家有所帮助!

深入浅析Nodejs中的net模块深入浅析Nodejs中的net模块Apr 11, 2022 pm 08:40 PM

本篇文章带大家带大家了解一下Nodejs中的net模块,希望对大家有所帮助!

怎么获取Node性能监控指标?获取方法分享怎么获取Node性能监控指标?获取方法分享Apr 19, 2022 pm 09:25 PM

怎么获取Node性能监控指标?本篇文章来和大家聊聊Node性能监控指标获取方法,希望对大家有所帮助!

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具