目前需求涉及大量的非同步操作,實際的頁面越來越傾向於單一頁面應用程式。以後可以使用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)實現了多種不同非同步程式庫之間的轉換。
1.4.3 commonJS Promise/A 規範
1.4.4 注意事項
});
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卻不可以。

選擇Python還是JavaScript取決於項目類型:1)數據科學和自動化任務選擇Python;2)前端和全棧開發選擇JavaScript。 Python因其在數據處理和自動化方面的強大庫而備受青睞,而JavaScript則因其在網頁交互和全棧開發中的優勢而不可或缺。

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。

JavaScript框架的強大之處在於簡化開發、提升用戶體驗和應用性能。選擇框架時應考慮:1.項目規模和復雜度,2.團隊經驗,3.生態系統和社區支持。

引言我知道你可能會覺得奇怪,JavaScript、C 和瀏覽器之間到底有什麼關係?它們之間看似毫無關聯,但實際上,它們在現代網絡開發中扮演著非常重要的角色。今天我們就來深入探討一下這三者之間的緊密聯繫。通過這篇文章,你將了解到JavaScript如何在瀏覽器中運行,C 在瀏覽器引擎中的作用,以及它們如何共同推動網頁的渲染和交互。 JavaScript與瀏覽器的關係我們都知道,JavaScript是前端開發的核心語言,它直接在瀏覽器中運行,讓網頁變得生動有趣。你是否曾經想過,為什麼JavaScr

Node.js擅長於高效I/O,這在很大程度上要歸功於流。 流媒體匯總處理數據,避免內存過載 - 大型文件,網絡任務和實時應用程序的理想。將流與打字稿的類型安全結合起來創建POWE


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

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

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

記事本++7.3.1
好用且免費的程式碼編輯器

禪工作室 13.0.1
強大的PHP整合開發環境