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

詳談nodejs非同步程式設計_node.js

May 16, 2016 pm 04:29 PM
nodejs 非同步程式設計

目前需求涉及大量的非同步操作,實際的頁面越來越傾向於單一頁面應用程式。以後可以使用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

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1670
14
CakePHP 教程
1428
52
Laravel 教程
1329
25
PHP教程
1276
29
C# 教程
1256
24
nodejs是後端框架嗎 nodejs是後端框架嗎 Apr 21, 2024 am 05:09 AM

Node.js 可作為後端框架使用,因為它提供高效能、可擴展性、跨平台支援、豐富的生態系統和易於開發等功能。

nodejs安裝目錄裡的npm與npm.cmd檔有什麼差別 nodejs安裝目錄裡的npm與npm.cmd檔有什麼差別 Apr 21, 2024 am 05:18 AM

Node.js 安裝目錄中有兩個與 npm 相關的文件:npm 和 npm.cmd,區別如下:擴展名不同:npm 是可執行文件,npm.cmd 是命令視窗快捷方式。 Windows 使用者:npm.cmd 可以在命令提示字元中使用,npm 只能從命令列執行。相容性:npm.cmd 特定於 Windows 系統,npm 跨平台可用。使用建議:Windows 使用者使用 npm.cmd,其他作業系統使用 npm。

nodejs怎麼連接mysql資料庫 nodejs怎麼連接mysql資料庫 Apr 21, 2024 am 06:13 AM

要連接 MySQL 資料庫,需要遵循以下步驟:安裝 mysql2 驅動程式。使用 mysql2.createConnection() 建立連接對象,其中包含主機位址、連接埠、使用者名稱、密碼和資料庫名稱。使用 connection.query() 執行查詢。最後使用 connection.end() 結束連線。

如何用 C++ 函數實作非同步程式設計? 如何用 C++ 函數實作非同步程式設計? Apr 27, 2024 pm 09:09 PM

摘要:C++中的非同步程式設計允許多工處理,無需等待耗時操作。使用函數指標建立指向函數的指標。回調函數在非同步操作完成時被呼叫。 boost::asio等函式庫提供非同步程式支援。實戰案例示範如何使用函數指標和boost::asio實現非同步網路請求。

nodejs是後端開發語言嗎 nodejs是後端開發語言嗎 Apr 21, 2024 am 05:09 AM

是的,Node.js 是一種後端開發語言。它用於後端開發,包括處理伺服器端業務邏輯、管理資料庫連接和提供 API。

nodejs和java的差別大嗎 nodejs和java的差別大嗎 Apr 21, 2024 am 06:12 AM

Node.js 和 Java 的主要差異在於設計和特性:事件驅動與執行緒驅動:Node.js 基於事件驅動,Java 基於執行緒驅動。單執行緒與多執行緒:Node.js 使用單執行緒事件循環,Java 使用多執行緒架構。執行時間環境:Node.js 在 V8 JavaScript 引擎上運行,而 Java 在 JVM 上運行。語法:Node.js 使用 JavaScript 語法,而 Java 使用 Java 語法。用途:Node.js 適用於 I/O 密集型任務,而 Java 適用於大型企業應用程式。

golang框架如何處理並發和非同步程式設計? golang框架如何處理並發和非同步程式設計? Jun 02, 2024 pm 07:49 PM

Go框架利用Go的並發和非同步特性提供高效處理並發和非同步任務的機制:1.透過Goroutine實現並發,允許同時執行多個任務;2.透過通道實現非同步編程,在不阻塞主執行緒的情況下執行任務;3.適用於實戰場景,如並發處理HTTP請求、非同步取得資料庫資料等。

nodejs適合什麼項目 nodejs適合什麼項目 Apr 21, 2024 am 05:45 AM

Node.js 適用於以下專案類型:網頁和伺服器應用程式事件驅動應用程式即時應用程式資料密集型應用程式命令列工具和腳本輕量級微服務

See all articles