异步编程之Promise(2):探究原理
異步編程系列教程:
- (翻譯)異步編程之Promise(1)——初見魅力
- 異步編程之Promise(2):探究原理
- 異步編程之Promise(3):拓展進階
- 異步編程之Generator(1)——領略魅力
- 異步編程之Generator(2)——剖析特性
- 異步編程之co——源碼分析
動手實現Promise
在異步編程之Promise(1)里,我是翻譯了一篇文章,里面是探究promise的模式和領略它的魅力。我們可以利用promise,緩解回調函數給我們帶來的回調金字塔。使用鏈式結構書寫,使代碼更加簡潔易懂,易于控制。但是對于構造promise和其內部的實現,卻用草草的一句new Promise()就帶過。這一次,借著閱讀樸靈大神的《深入淺出Node.Js》,我們自己動手實現一個小小的基本的promise吧。
構建Promise對象
首先我們需要回顧一下,一個Promise/A模式和API上是如何定義的:
- Promise分別有三個狀態:pending初始狀態,fulfilled完成狀態,rejected失敗狀態。
- 一旦promise是fulfilled狀態或rejected狀態,那么它就是不會再改變的。
- 具備
then()方法,用于接收fulfilled和rejected狀態的回調方法,并在相應狀態下進行觸發。 then()方法只允許接受function對象,其余的會被忽略。then()方法會返回Promise對象,提供鏈式調用。then()方法可接收第三個方法,用于支持progress事件的回調方法。
知道我們的Promise對象需要有什么之后,我們就可以開始嘗試寫Promise的構造函數了。還有Promise是基于事件機制的,也可以說是發布/訂閱模式。所以我們為了演示方便,將使用Node里的events模塊。
還不清楚自定義事件的同學,推薦一個視頻給你們入門:阿當大話西游之WEB組件
var events = require('events'); //events模塊
var util = require('util'); //util工具包模塊var MyPromise = function(){events.EventEmitter.call(this);
};
util.inherits(MyPromise, events.EventEmitter); // 繼承MyPromise.prototype.then = function(resolve, reject, progress){// this.once()是綁定事件被觸發后立即移除事件if(typeof resolve === 'function'){this.once('success', resolve);}if(typeof reject === 'function'){this.once('error', reject);}if(typeof progress === 'function'){// 不需要once()this.on('progress', progress);}return this;
}; 由此,我們就實現了Promise/A規范。我們用promise對象的then,用相應的事件存放了各個狀態的回調函數。那接下來,我們就要知道如何觸發這些事件。
構建Deferred對象
---
為了實現事件的觸發,我們需要有一個新的對象Deferred。意思是,延遲對象。
var Deferred = function(){this.state = 'pending';this.promise = new MyPromise();
};Deferred.prototype.resolve = function(obj){this.state = 'fulfilled';this.promise.emit('success', obj);
};
Deferred.prototype.reject = function(err){this.state = 'failed';this.promise.emit('error', err);
};
Deferred.prototype.progress = function(data){this.promise.emit('progress', data);
}; 我們可以看到,我們之前定義的promise成為了deferred對象中的一個屬性。然后Deferred對象的方法,都是用來觸發事件來改變promise狀態的。這種模式也稱作Promise/Deffered模式,它是基于發布與訂閱模式,并提供了更加高級的抽象。Deferred對象,用來控制Promise內部,維護Promise狀態。Promise對象,則是作用于外部,通過then(resolve, reject)對外提供接口。
對于上一篇講到的promise化的readJSON,我們可以使用我們定義的Promise/Deferred重寫一遍:
var readJSON = function(filename, encoding){var deferred = new Deferred();fs.readFile(filename, encoding, function(err, res){if(err)return deferred.reject(err);deferred.resolve(JSON.parse(res));});return deferred.promise;
};// 應用
readJSON('data.json', 'utf-8').then(function(res){console.log(res.message); // Hello World!
}) 對我來說,我更喜歡Promise/Deferred的實現。因為通過Deferred對象,我們可以很隨心的控制promise的狀態,得到我們想要的樣子。當然喜歡原生ES6的那種為Promise構造函數傳入工廠函數,也是可以自己改造一下的,和回調參數差不多,可以自行嘗試一下。不知道是不是應為我個人水平問題,寫起來覺得亂糟糟。所以我更喜歡Promise/Deferred模式的實現。代碼如下:
// 去掉Deferred對象,直接通過回調參數來確定是resolve還是reject。
var MyPromise = function(factory){events.EventEmitter.call(this);var _this = this;factory && factory(function(res){console.log(res);_this.emit('success', res);}, function(err){_this.emit('error', err);});
};
util.inherits(MyPromise, events.EventEmitter);// 模擬ES6構造函數方法的應用
var readJSON = function(filename, encoding){return new MyPromise(function(resolve, reject){fs.readFile(filename, encoding, function(err, res){if(err)return reject(err);resolve(JSON.parse(res));});});
};readJSON("data.json", 'utf-8').then(function(data){console.log(data.message); // Hello World!
}); 我們通過把json解析后傳到resolve()中實現了我們上一篇的readJSON函數promise化的要求!酷~
開始使用Promise
從這兩篇promise的探究路上,如果能體會到其中的奧妙,應該也差不多可以上道了。這個時候在ES6還未普及前,實現完整優秀promise模式可以借助一些promise庫。這里我推薦 Q.js,為了能體現它的高效和優雅,我們借助以往的readJSON例子。
var Q = require('q');
var fs = require('fs');var readFile = function(filename, encoding){var deferred = new Q.defer(); // 獲取Q的deferred對象fs.readFile(filename, encoding, function(err, res){if(err)return deferred.reject(err);deferred.resolve(res);});return deferred.promise; // 將promise對象return出去,實現鏈式調用
};var readJSON = function(filename, encoding){return readFile(filename, encoding).then(JSON.parse);
};readJSON('data.json', 'utf-8').then(function(data){console.log(data.message); //Hello World!
}); 這里并沒有體現優雅,但是可以看到promise/deferred模式的使用。特別是和我一樣喜歡這種模式的同學,簡直不能再爽!當然,說到優雅,我們可以回想一下,每次我們處理fs.readFile()的callback時,我們都是重復的有錯誤就reject,沒錯誤就resolve。同樣的邏輯,其實我們是可以封裝起來的,Q就幫我們做到了這一點。
// 改變fs.readFile()
var readFile = function(filename, encoding){var deferred = new Q.defer(); // 獲取Q的deferred對象 fs.readFile(filename, encoding, deferred.makeNodeResolver());return deferred.promise; // 將promise對象return出去,實現鏈式調用
}; 語意可得,弄一個Node式的回調。這個實現其實很簡單,書上也有說。這個就交給各位同學們,自己動手試一試吧~
接下來,會針對我們自己實現的Promise進行拓展,完成我們更多的需求。在此過程中,探究Promise實現原理。為接下來的異步編程學習打下基礎。
再次感謝樸靈老師的《深入淺出Node.Js》。
再次感謝樸靈老師的《深入淺出Node.Js》。
再次感謝樸靈老師的《深入淺出Node.Js》。
重要的話,要說三遍。
轉載于:https://www.cnblogs.com/YikaJ/p/4468987.html
總結
以上是生活随笔為你收集整理的异步编程之Promise(2):探究原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “残红零落无人赏”下一句是什么
- 下一篇: Sublime text3 快捷方式(w