探索Javascript 异步编程
在我們日常編碼中,需要異步的場景很多,比如讀取文件內容、獲取遠程數據、發送數據到服務端等。因為瀏覽器環境里Javascript是單線程的,所以異步編程在前端領域尤為重要。
異步的概念
所謂異步,是指當一個過程調用發出后,調用者不能立刻得到結果。實際處理這個調用的過程在完成后,通過狀態、通知或者回調來通知調用者。
比如我們寫這篇文字時點擊發布按鈕,我們并不能馬上得到文章發布成功或者失敗。等待服務器處理,這段時間我們可以做其他的事情,當服務器處理完成后,通知我們是否發布成功。
所謂同步,是指當一個過程調用發出后,必須等待這個過程處理完成后,再處理其他事情。即堵塞執行。
異步的方式
在es6之前,我們實現異步有4種方法,回調、事件、發布訂閱和promise方式。
異步之回調:
function dealTask(param, callback) {// Deal with some time consuming tasks. // ... Object.prototype.toString.call(callback) === '[object Function]' ? callback() : null; } dealTask({ id: 1 }, function() { console.log('... I am in the callback...'); })回調的方式來實現異步其實就是把需要在當前任務完成后執行的函數當成參數傳入,完成任務后執行即可。
異步之事件
function dealTask(param) {// Deal with some time consuming tasks. // ... events.trigger('dealTaskFinish') } events.on('dealTaskFinish', function() { console.log('...I am in the end...'); })通過事件來實現回調,好處是方便實用,跨模塊傳遞數據。壞處是,事件用的多了后業務邏輯混亂,不知道哪里注冊過哪里監聽過。
另外需要注意的是在web component場景下,mount后注冊過的事件需要在unmount釋放,不然會導致內存泄露。
異步之發布訂閱
發布訂閱的簡單例子是,一個開關,同時并聯幾個燈泡(在不同房間),觸發的時候,幾個燈泡都會得到指令,然后執行發光的行為。
// 使用pubsubz實現 var testSubscriber = function(data ){ console.log(data ); }; var testSubscription = pubsubz.subscribe( 'example', testSubscriber ); pubsubz.publish( 'example', 'hello' );訂閱發布與的性質與"事件監聽"類似,不同的是,我們可以通過查看"消息中心",了解存在多少信號、每個信號有多少訂閱者,從而監控程序的運行。
異步之Promise
function helloWorld (ready) {return new Promise(function (resolve, reject) { if (ready) { resolve("Hello World!") } else { reject("Good bye!") } }) } helloWorld(true).then(function (message) { console.log(message) }, function (error) { console.log(error) })Promises對象是CommonJS工作組提出的一種規范,是對異步編程的一種統一,其實也就是語法糖,可閱讀性變強了而已。
在ES6出來以后,我們的異步方式也發生了改變。
異步之Generator
Generator函數是協程在ES6的實現,最大特點就是可以交出函數的執行權(即暫停執行)。
Generator函數可以暫停執行和恢復執行,這是它能封裝異步任務的根本原因。除此之外,它還有兩個特性,使它可以作為異步編程的完整解決方案:函數體內外的數據交換和錯誤處理機制。
function* Foo(x) {yield x + 1; var y = yield null; return x + y; } var foo = Foo(5); foo.next(); // { value: 6, done: false } foo.next(); // { value: null, done: false } foo.next(8); // { value: 13, done: true }next方法返回值的value屬性,是Generator函數向外輸出數據;next方法還可以接受參數,這是向Generator函數體內輸入數據。
yield命令用于將程序的執行權移出Generator函數,那么就需要一種方法,將執行權再交還給Generator函數
上面的方式,是我們手動調用Generator函數執行,但是當我們的需要執行next方法很多時,就需要Generator函數自動執行了。
Generator函數自動執行的意思是,通過一定的方法來自動執行next方法,比如:
function autoRunGen(gen){var g = gen();function next(data){ var result = g.next(data); if (result.done) return result.value; result.value.then(function(data){ next(data); }); } next(); }co模塊是TJ開發的一個小工具,用于Generator函數的自動執行。他的主要思想和上面的代碼片段類似。
使用co的前提條件是,Generator函數的yield命令后面,只能是Promise對象。
co(function *() {var data = yield $.get('/api/data'); console.log(data); var user = yield $.get('/api/user'); console.log(user); var products = yield $.get('/api/products'); console.log(products); });co模塊使得我們可以像寫同步代碼一樣,寫異步代碼。
異步之async/await
async函數僅僅是Generator函數的語法糖。
var fs = require('fs');var readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error) reject(error); resolve(data); }); }); }; var gen = function* (){ var f1 = yield readFile('/etc/a.js'); var f2 = yield readFile('/etc/b.js'); console.log(f1.toString()); console.log(f2.toString()); };使用async/await方式:
var asyncReadFile = async function (){ var f1 = await readFile('/etc/a.js'); var f2 = await readFile('/etc/b.js'); console.log(f1.toString()); console.log(f2.toString()); };async函數就是將Generator函數的星號(*)替換成async,將yield替換成await,僅此而已。
不同的是,Generator執行需要手動執行,而async函數可以自動執行,像寫同步一樣寫異步。Generator返回Iterator對象,async函數返回Promise對象。
?-轉載
轉載于:https://www.cnblogs.com/wenJiaQi/p/6380325.html
總結
以上是生活随笔為你收集整理的探索Javascript 异步编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2.scala控制结构、函数、异常处理
- 下一篇: 纹绣店适合开在什么地方 选址真的非常重要