JavaScript/Node.JS中的Promises詳解 物件用來進行延遲(deferred) 和 非同步(asynchronous) 計算。本文主要和大家分享JavaScript/Node.JS中的JavaScript/Node.JS中的Promises詳解s詳解,希望能幫助大家。
一個JavaScript/Node.JS中的Promises詳解 處於以下三種狀態之一:
pending: 初始狀態, 非fulfilled 或rejected.
fulfilled: 成功的操作.
rejected: 失敗的操作.
JavaScript/Node.JS中的Promises詳解 介面表示為一個值的代理,這個值在promise創建時未必已知. 它允許你將handlers 與一個非同步action 最終的成功或失敗狀態關聯起來. 這使得非同步方法可以像同步方法那樣返回值: 非同步方法傳回一個在未來某個時刻擁有一個值的promise 來替代最終回傳值.
pending狀態的promise既可帶著一個值成為fulfilled 狀態,也可帶著一個reason成為rejected 狀態. 任意情況發生時, 通過promise的then 方法所排列(queued up )的對應handlers 就會被呼叫. (當綁定對應的handler 時,如果promise 已經處於fulfilled 或rejected 狀態這個handler 將會被呼叫, 所以在異步操作的完成和它的handler 的綁定之間不存在競爭條件.)
因為JavaScript/Node.JS中的Promises詳解.prototype.then 和JavaScript/Node.JS中的Promises詳解.prototype.catch 方法回傳promises, 所以它們可以被鍊式呼叫—一種被稱為composition 的操作.
簡單的說,JavaScript/Node.JS中的Promises詳解 被當成 callback hell 的救命仙丹。
回呼函數是JavaScript的一大特色! node.js官方的api基本上都是以會回呼方式傳遞函數回傳值。習慣同步編程的對這種非同步方式多少會產生水土不服,而且層層嵌套,不知不覺就建造起了一座高高的回調金字塔。針對這種普遍問題,JavaScript/Node.JS中的Promises詳解應勢而生!
建立JavaScript/Node.JS中的Promises詳解
promise.then(function(result) { console.log(result); // “完美!” }, function(err) { console.log(err); // Error: "出问题了" });
「then」接受兩個參數,成功的時候呼叫一個,失敗的時候呼叫另一個,兩個都是可選的,所以你可以只處理成功的情況或失敗的情況。
值的處理
你可以對結果做些修改然後回傳一個新值:
getJSON('story.json').then(function(story) { return getJSON(story.chapterUrls[0]); }).then(function(chapter1) { console.log("Got chapter 1!", chapter1); });
你回傳一個「類別JavaScript/Node.JS中的Promises詳解」的物件
get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.log("Failed!", error); });
你也可以使用「catch」:
asyncThing1().then(function() { return asyncThing2(); }).then(function() { return asyncThing3(); }).catch(function(err) { return asyncRecovery1(); }).then(function() { return asyncThing4(); }, function(err) { return asyncRecovery2(); }).catch(function(err) { console.log("Don't worry about it"); }).then(function() { console.log("All done!"); });
靜態方法
JavaScript/Node.JS中的Promises詳解.resolve(promise);
傳回一個JavaScript/Node.JS中的Promises詳解(當且僅當promise.constructor == JavaScript/Node.JS中的Promises詳解)
JavaScript/Node.JS中的Promises詳解.resolve(thenable);
從thenable 物件建立一個新的JavaScript/Node.JS中的Promises詳解。一個 thenable(類別 JavaScript/Node.JS中的Promises詳解)物件是一個帶有“then”方法的物件。
JavaScript/Node.JS中的Promises詳解.resolve(obj);
建立一個以 obj 為肯定結果的 JavaScript/Node.JS中的Promises詳解。
JavaScript/Node.JS中的Promises詳解.reject(obj);
建立一個以 obj 為否定結果的 JavaScript/Node.JS中的Promises詳解。為了一致性和調試便利(如堆疊追蹤),obj 應該是一個 Error 實例物件。
JavaScript/Node.JS中的Promises詳解.all(array);
建立一個JavaScript/Node.JS中的Promises詳解,當且僅當傳入數組中的所有JavaScript/Node.JS中的Promises詳解 都肯定之後才肯定,如果遇到數組中的任何一個JavaScript/Node.JS中的Promises詳解 以否定結束,則拋出否定結果。每個陣列元素都會先經過 JavaScript/Node.JS中的Promises詳解.resolve,所以陣列可以包含類別 JavaScript/Node.JS中的Promises詳解 物件或其他物件。肯定結果是一個數組,包含傳入數組中每個 JavaScript/Node.JS中的Promises詳解 的肯定結果(且保持順序);否定結果是傳入數組中第一個遇到的否定結果。
JavaScript/Node.JS中的Promises詳解.race(array);
建立一個 JavaScript/Node.JS中的Promises詳解,當陣列中的任意物件肯定時將其結果作為肯定結束,或當陣列中任意物件否定時將其結果作為否定結束。
其實已經有一些第三方函式庫實作了JavaScript/Node.JS中的Promises詳解 的功能:
Q
when
WinJS
#RSVP.js
上面這些函式庫和JavaScript 原生JavaScript/Node.JS中的Promises詳解 都遵守一個通用的、標準化的規範:JavaScript/Node.JS中的Promises詳解s/A+,jQuery 有個類似的方法叫 Deferred,但不相容JavaScript/Node.JS中的Promises詳解s/A+ 規範,於是會有點小問題,使用需謹慎。 jQuery 還有一個 JavaScript/Node.JS中的Promises詳解 類型,它其實是 Deferred 的縮減版,所以也有相同問題。
目前的瀏覽器已經(部分)實作了 JavaScript/Node.JS中的Promises詳解。
Chrome 32、Opera 19 和 Firefox 29 以上的版本已經預設支援 JavaScript/Node.JS中的Promises詳解。由於是在 WebKit 核心中所以我們有理由期待下個版本的 Safari 也會支持,並且 IE 也在不斷的開發中。
要在這兩個瀏覽器上達到相容標準 JavaScript/Node.JS中的Promises詳解,或在其他瀏覽器以及 Node.js 中使用 JavaScript/Node.JS中的Promises詳解,可以看看這個polyfill(gzip 之後 2K)。
玩node的同志们都知道,当这门语言被提出来的时候,作为自己最为骄傲的异步机制,却被PHP和Python等战团喷得不成样子的是,他们嘲笑着nodejs那蠢蠢的无限嵌套,nodejs战团只能以我们只要性能!!!来安慰自己。
众所周知,javascript作为一个单线程语言,所有工作都是阻塞的,有好多人不理解为什么说是javascript是阻塞的,怎么可以做到异步机制呢?
在我们平时可以接触到的情况下,我们可以用浏览器来触发XMLHttpRequest(Ajax)来异步获取数据,setTimeout、setInterval来完成定时任务,而这并不是javascript的语言来决定这些异步操作的,而是解释Javascript的浏览器来去操作线程作多线程操作的,可以把这些方法理解为浏览器抛出的多线程API。而nodejs是基于高性能v8来实现,它也是像浏览器一样,抛出了很多操作线程的API,从而来实现异步机制。
异步的机制可以让我们更为节省系统资源,并不需要为每一个请求去像PHP,Tomcat一样新开一个线程,node内部会有处理各种任务的线程(使用Net,File System,Timers 等很多模块来操作不同的线程),把不同的异步任务分发给各个任务线程,并会弹性地为线程分配硬件,这都是来自v8的高性能,也是为什么nodejs能面对高I/O情况的根本原因。
到头来我们必须面对血淋淋的现实,当我初接触node的时候,代码也是这样写的
fs.readFile(MrFileFirst,"utf8",function(err,data1){ if(err){ //do err thing }else{ fs.readFile(MrFileSecond,"utf8",function(err,data2){ if(err){ //do err thing }else{ mongo.find(SomeQuery,function(err,data3){ if(err){ //do err thing }else{ //do the real thing with [data1,data2,data3] } }) } }) }})
Oh,my god!好好的异步机制还是玩成了同步……而且惨不忍睹!仅仅只是想返回最后的三个数据,但是这个例子三个任务之间并没有关系嵌套,这样子强行把异步玩成同步的话,还是阻塞的代码,这段代码的工作时序大概在这样的:
和不用node并没有什么区别,完全是阻塞的。在平时我们可以碰到更多的关系层级的嵌套(下一步的操作要基于上一步的结果),这时才必须使用同步去完成任务,但是要是像上面这样写的话,我相信你会写到吐血的(我已经忘了我在代码中写过多个少if (err) {}了,因为node的底层API异步方法都是以err为第一个参数,使得上层所有异步方法都为这种模式)
有人看不下去了,便自会有人站出来,我们渐渐地实现了从无到有的过程,我最开始接触的是阿里的
var ep = require("eventproxy");ep.create("task1","task2","task3",function(result1,result2,result3){ //do the real thing with [result1,result2,result3]}).fail(function(e){ //do err thing});fs.readFile(MrFileFirst,"utf8",ep.done("task1"));fs.readFile(MrFileSecond,"utf8",ep.done("task2"));fs.readFile(MrFileThird,"utf8",ep.done("task3"));
这样,就可以实现三个文件异步进行读取,并且在三个任务都完成时进行最终的工作,时序图如下图:
三个任务几乎同时触发(除去代码的触发时间),所以左边的三个点其实可以看作是一个点,而这三个任务都去同时异步进行,在三个任务都完成的时候,来触发最后的任务。
这才是node发挥出自己优点的地方,处理时间节省了很多(如果三个任务的时间消耗都为1,则时间缩减了2/3),这才是大node.js。
eventproxy也有更多的用法,可以去其npm上看看。
async是国外强大的异步模块,它的功能与eventproxy相似,但是维护速度与周期特别快,毕竟是用的人多呀,但是支持国产——是一种情怀,附介绍使用async的文章
http://blog.fens.me/nodejs-async/
人总是不知足的,而刚好是这个不知足,才让我们不停地去探索想要的、更为方便的东西。而这时,便有人想让自己写的代码复用性更高,同时也不想去写那么多的callback去嵌套,这时便有了Promiss/A+规范,其是:
An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
一个健全的通用JavaScript JavaScript/Node.JS中的Promises詳解开放标准,源于开发者,并归于开发者
在ES6中也新增了原生JavaScript/Node.JS中的Promises詳解的使用,而之前JavaScript/Node.JS中的Promises詳解库有promise,Q,bluebird等,在这些库中现在已经慢慢对ES6的原生JavaScript/Node.JS中的Promises詳解作了兼容,虽然ES6现在还没有大规模投入使用过程中。
在其中最为出名的则是bluebird和Q库,我使用的是bluebird,先贴一段bluebird的使用代码感受感受
//CAST//MrFileOne.txt//MrFileTow.txt//MrFileThree.txt//关系嵌套任务var JavaScript/Node.JS中的Promises詳解 = require("bluebird"), readFileAsync = JavaScript/Node.JS中的Promises詳解.promisify(require("fs").readFile);readFileAsync("MrFileOne.txt","utf8") .then(function(data){ //if the data contains the path of MrFileTow var path = ..... //do something with data return readFileAsync(path,"utf8"); }) .then(function(data){ //if the data contains the path of MrFileThree var path = ..... //do something with data return readFileAsync(path,"utf8"); }) .then(function(data){ //get the data of MrFileThree //do something }) .catch(function(err){ console.log(err); });//无关系汇总任务JavaScript/Node.JS中的Promises詳解.all([ readFileAsync("MrFileOne.txt","utf8"), readFileAsync("MrFileTwo.txt","utf8"), readFileAsync("MrFileThree.txt","utf8") ]) .then(function(datas){ //do something with three data form our actors }) .catch(function(err){ console.log(err); });
有没有一下被这种写法所吸引,这就是JavaScript/Node.JS中的Promises詳解模块的魅力,它很优雅地将函数的回调写在了then里面,并为then返回一个新的JavaScript/Node.JS中的Promises詳解,以供下一次的then去回调本次返回的结果。
首先使用了方法:
readFileAsync = JavaScript/Node.JS中的Promises詳解.promisify(rquire("fs").readFile);
这个方法则是为复制了readFile方法并为其增添了JavaScript/Node.JS中的Promises詳解机制,而JavaScript/Node.JS中的Promises詳解机制是什么呢?那就是为其添加JavaScript/Node.JS中的Promises詳解方法和属性后,让整个方法的返回值为一个JavaScript/Node.JS中的Promises詳解对象,我们可以通过JavaScript/Node.JS中的Promises詳解来调用then方法,来去对这个JavaScript/Node.JS中的Promises詳解方法的回调进行处理。在JavaScript/Node.JS中的Promises詳解中都默认的将第一个参数err放在了后面的catch中,使得我们再也不用写那么多的if(err)了。我们可以直接通过在then方法中通过函数参数来获取这个JavaScript/Node.JS中的Promises詳解的异步数据,从而进行下一步的处理。
而在then方法后,其返回的也是一个JavaScript/Node.JS中的Promises詳解对象,我们可以在其后再次进行then来获取上一个then的数据并进行处理。当然,我们也可以人为地去决定这个then的返回参数,但是整个then方法返回的都是一个JavaScript/Node.JS中的Promises詳解对象。
readFileAsync("MrFileOne.txt","utf8") .then(function(data){ if(.....){ //data isn't what we want JavaScript/Node.JS中的Promises詳解.reject("It's not correct data!"); }else{ return data; } }) .then(function(){ console.log("yeah! we got data!"); }) .catch(function(err){ console.log(err); })
在上面代码中,如果获取到的data并不是我们想要的,则我们可直接调用JavaScript/Node.JS中的Promises詳解.reject抛出一个ERROR,并直接交给catch来处理错误,所以在控制台我们能得到的是“It's not correct data!”,并不会得到“yeah! we got data!”,因为抛出错误后其之后的then方法并不会跟着执行。
当然我们也可以自定义多个catch来捕获不同的ERROR,对其作不同的处理,就像下面的一样
var customError = new Error(SOMENUMBER,SOMEDESCRIPTION)readFileAsync("MrFileOne.txt","utf8") .then(function(data){ switch(data){ case CASE1: JavaScript/Node.JS中的Promises詳解.reject(customError); case CASE2: JavaScript/Node.JS中的Promises詳解.reject(new SyntaxError("noooooo!")); } }) .catch(customError,function(err){ //do with customError }) .catch(SyntaxError,function(err){ //do with SyntaxError }) .catch(function(err){ console.log(err); })
而更多的使用方法,可以在bluebird on npm里学习得到,相信你看了之后会爱上JavaScript/Node.JS中的Promises詳解的。
Q模块也是一个非常优秀的JavaScript/Node.JS中的Promises詳解,它的实现原理和bluebird都大同小异,都是基于JavaScript/Node.JS中的Promises詳解/A+标准来扩展的,所以使用上甚至都是差不了多少的,选择哪一个就看个人爱好了。
重点来啦,我们先来看一段普通的代码
var obj = (function(){ var variable; return { get: function(){ return variable; }, set: function(v){ variable = v; } }})();exports.get = obj.get;exports.set = obj.set;
这个代码实现的是创建了一个闭包来储存变量,那么我在外部调用这个模块时,则可以去操作这个值,即实现了一个Scope变量,并把它封装了起来。
根据我们以前的思想,这段代码看起来很正常,但是这时侯我要加一判断进去,即在get方法调用时,如果varibale为undefined,那么我则去做一个读文件的操作,从文件中将它读出来,并反回,你会怎么实现呢?
你会发现,通过以往的思维,你是无法做到这一方法的,那么使用异步思维去想想呢,好像有点门头:
get: function(callback){ if(varibale){ callback(varibale); }else{ fs.readFile("SomeFile","utf8",function(err,data){ if(err){ //do with err return; } callback(data); }) }}
这样……嗯咳咳,看起来似乎好像也许解决的还可以,但是你自己也会觉得,这其实糟透了,我们将原本的简单get函数更改得这么复杂。那么问题来了,谁会在使用的时候会想到这个get方法其实是一个回调的方法呢?你平时使用get时你会考虑说是这个里面有可以是回调吗?我们都是直接get()来获取它的返回值。
这就是我们自己给自己造成的矛盾和麻烦,这也是我以前曾经遇到的。
那么在模块化的node里,我们怎么去实现这些不必要的麻烦呢?那就是用JavaScript/Node.JS中的Promises詳解思想去编写自己的代码,我们先试着用上面说到的bluebird来加工一下这段代码:
var JavaScript/Node.JS中的Promises詳解 = require("bluebird"), fs = require("fs");var obj = (function(){ var variable; return { get: function(){ if(variable){ return JavaScript/Node.JS中的Promises詳解.resolve(variable); } return JavaScript/Node.JS中的Promises詳解.promisify(fs.readFile)("SomeFile","utf8"); }, set: function(v){ return JavaScript/Node.JS中的Promises詳解.resolve(variable = v); } }});exports.get = obj.get;exports.set = obj.set;
就是这么漂亮,使用JavaScript/Node.JS中的Promises詳解.resolve方法则是将变量转化为一个JavaScript/Node.JS中的Promises詳解对象,则是我们在外部对这个模块进行使用时,则要求我们使用JavaScript/Node.JS中的Promises詳解的思想去应用模块抛出的接口,比如:
var module = require("thisModule.js");module.get() .then(function(data){ console.log(data); module.set("new String"); return module.get; }) .then(function(data){ console.log(data); });
当我们使用JavaScript/Node.JS中的Promises詳解思想去面对每一个接口的时候,我们可以完全不用考虑这个模块的代码是怎么写的,这个方法该怎么用才是对的,到底是回调还是赋值。我们可以很直接的在其模块方法后then来解决一切问题!不用关心前面的工作到底做了什么,怎么做的,到底是异步还是同步,只要我们将整个工作流程都使用JavaScript/Node.JS中的Promises詳解来做的话,那会轻松很多,而且代码的可读性会变得更好!
简直是神器啊!
使用JavaScript/Node.JS中的Promises詳解编程思想去和node玩耍,你会相信真爱就在眼前。同时我也相信在前端模块化加速的今天,JavaScript/Node.JS中的Promises詳解编程思想必定会渗透至前端的更多角落。
有了JavaScript/Node.JS中的Promises詳解的处理,因为在前端代码中最多的异步处理就是Ajax,它们都被包装为了JavaScript/Node.JS中的Promises詳解 .then的风格。那么对于一部分同步的非异步处理呢?如localStorage、setTimeout、setInterval之类的方法。在大多数情况下,博主仍然推荐使用JavaScript/Node.JS中的Promises詳解的方式包装,使得项目Service的返回接口统一。这样也便于像上例中的多个异步任务的串行、并行处理。在Angular路由中对于只设置template的情况,也是这么处理的。
对于setTimeout、setInterval在Angular中都已经为我们内置了$timeout和$interval服务,它们就是一种JavaScript/Node.JS中的Promises詳解的封装。对于localStorage呢?可以采用$q.when方法来直接包装localStorage的返回值的为JavaScript/Node.JS中的Promises詳解接口,如下所示:
var data = $window.localStorage.getItem('data-api-key'); return $q.when(data);
整个项目的Service层的返回值都可以被封装为统一的风格使用了,项目变得更加的一致和统一。在需要多个Service同时并行或者串行处理的时候,也变得简单了,一致的使用方式。
在前面已经提到JavaScript/Node.JS中的Promises詳解是延迟到未来执行某些特定任务,在调用时候则给消费者返回一个”承诺“,消费者线程并不会被阻塞。在消费者接受到”承诺“之后,消费者就不用再关心这些任务是如何完成的,以及督促生产者的任务执行状态等。直到任务完成后,消费者手中的这个”承诺“就被兑现了。
对于这类延迟机制,在前端的UI交互中也是极其常见的。比如模态窗口的显示,对于用户在模态窗口中的交互结果并不可提前预知的,用户是点击”ok“按钮,或者是”cancel“按钮,这是一个未来将会发生的延迟事件。对于这类场景的处理,也是JavaScript/Node.JS中的Promises詳解所擅长的领域。在Angular-UI的Bootstrap的modal的实现也是基于JavaScript/Node.JS中的Promises詳解的封装。
$modal.open({ templateUrl: '/templates/modal.html', controller: 'ModalController', controllerAs: 'modal', resolve: { } }) .result .then(function ok(data) { // 用户点击ok按钮事件 }, function cancel(){ // 用户点击cancel按钮事件 });
这是因为modal在open方法的返回值中给了我们一个JavaScript/Node.JS中的Promises詳解的result对象(承诺)。等到用户在模态窗口中点击了ok按钮,则Bootstrap会使用$q的defer来resolve来执行ok事件;相反,如果用户点击了cancel按钮,则会使用$q的defer来reject执行cancel事件。
这样就很好的解决了延迟触发的问题,也避免了callback的地狱。我们仍然可以进一步将其返回值语义化,以业务自有的术语命名而形成一套DSL API。
function open(data){ var defer = $q.defer(); // resolve or reject defer; var promise = defer.promise; promise.ok = function(func){ promise.then(func); return promise; }; promise.cancel = function(func){ promise.then(null, func); return promise; }; return promise; };
则我们可以如下方式来访问它:
$modal.open(item) .ok(function(data){ // ok逻辑 }) .cancel(function(data){ // cancel 逻辑 });
是不是感觉更具有语义呢?在Angular中$http的返回方法success、error也是同样逻辑的封装。将success的注册函数注册为.then方法的成功回调,error的注册方法注册为then方法的失败回调。所以success和error方法只是Angular框架为我们在JavaScript/Node.JS中的Promises詳解语法之上封装的一套语法糖而已。
Angular的success、error回调的实现代码:
promise.success = function(fn) { promise.then(function(response) { fn(response.data, response.status, response.headers, config); }); return promise; }; promise.error = function(fn) { promise.then(null, function(response) { fn(response.data, response.status, response.headers, config); }); return promise; };
相关推荐:
Javascript中的异步编程规范JavaScript/Node.JS中的Promises詳解s/A详细介绍_jquery
基于promise.js实现nodejs的promises库_node.js
有关JavaScript/Node.JS中的Promises詳解s异步问题详解_javascript技巧
以上是JavaScript/Node.JS中的Promises詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!