ES6原生提供了 Promise 對象。javascript
究竟是何方妖怪呢?打出來看看:php
所謂 Promise,就是一個對象,用來傳遞異步操做的消息。它表明了某個將來纔會知道結果的事件(一般是一個異步操做),而且這個事件提供統一的 API,可供進一步處理。css
Promise 對象有如下兩個特色。html
(1)對象的狀態不受外界影響。Promise 對象表明一個異步操做,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和 Rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是 Promise 這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變。java
(2)一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise 對象的狀態改變,只有兩種可能:從 Pending 變爲 Resolved 和從 Pending 變爲 Rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對 Promise 對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。jquery
有了 Promise 對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise 對象提供統一的接口,使得控制異步操做更加容易。面試
Promise 也有一些缺點。首先,沒法取消 Promise,一旦新建它就會當即執行,沒法中途取消。其次,若是不設置回調函數,Promise 內部拋出的錯誤,不會反應到外部。第三,當處於 Pending 狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。ajax
廢話很少說,直接上demo:編程
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Promise 學習筆記</title> <script type="text/javascript"> window.onload = function() { function pms1() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務1'); resolve('執行任務1成功'); }, 2000); }); } function pms2() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務2'); resolve('執行任務2成功'); }, 2000); }); } function pms3() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務3'); resolve('執行任務3成功'); }, 2000); }); } pms1().then(function(data) { console.log('第1個回調:' + data); return pms2(); }) .then(function(data) { console.log('第2個回調:' + data); return pms3(); }) .then(function(data) { console.log('第3個回調:' + data); return '還沒完!該結束了吧!' }).then(function(data) { console.log(data); }); } </script> </head> <body> </body> </html>
怎麼樣?是否是灰常簡單啊!api
demo2:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> window.onload = function() { function pms1() { return new Promise(function(resolve, reject) { setTimeout(function() { var num = Math.ceil(Math.random() * 10); //生成1-10的隨機數 if(num <= 5) { resolve(num); } else { reject('數字太大了吧!'); } }, 2000); }); } setInterval(function() { pms1().then(function(data) { //小於等於5的 console.log(data); }, function(data) { //大於的 console.log(data); }) }, 1000); } </script> </head> <body> </body> </html>
Promise 構造函數接受一個函數做爲參數,該函數的兩個參數分別是 resolve 方法和 reject 方法。
若是異步操做成功,則用 resolve 方法將 Promise 對象的狀態,從「未完成」變爲「成功」(即從 pending 變爲 resolved);
若是異步操做失敗,則用 reject 方法將 Promise 對象的狀態,從「未完成」變爲「失敗」(即從 pending 變爲 rejected)。
demo:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Promise 學習筆記</title> <script type="text/javascript"> window.onload = function() { function pms1() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務1'); resolve('執行任務1成功'); }, 2000); }); } function pms2() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務2'); resolve('執行任務2成功'); }, 2000); }); } function pms3() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務3'); resolve('執行任務3成功'); }, 2000); }); } Promise.all([pms1(), pms2(), pms3()]).then(function(data) { console.log(data); console.log({}.toString.call(data)); }) } </script> </head> <body> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Promise 學習筆記</title> <script type="text/javascript"> window.onload = function() { function pms1() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務1'); resolve('執行任務1成功'); }, 1000); }); } function pms2() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務2'); resolve('執行任務2成功'); }, 2000); }); } function pms3() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('執行任務3'); resolve('執行任務3成功'); }, 3000); }); } Promise.race([pms1(), pms2(), pms3()]).then(function(data) { console.log(data); //注意上面的延時時間 }) } </script> </head> <body> </body> </html>
再來看看jquery裏面的$.Deferred:
var def = $.Deferred(); console.log(def);
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script> <script type="text/javascript"> $(function() { function runAsync() { var def = $.Deferred(); setTimeout(function() { console.log('執行完成'); def.resolve('隨便什麼數據'); }, 2000); return def; } runAsync().then(function(data) { console.log(data) }); }) </script> </head> <body> </body> </html>
是否是感受和ES6的Promise很像呢?
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script> <script type="text/javascript"> $(function() { function runAsync() { var def = $.Deferred(); setTimeout(function() { console.log('執行完成'); def.resolve('隨便什麼數據'); }, 2000); return def; } var pms=runAsync(); pms.then(function(data) { console.log(data) }); pms.resolve('我穿越了!') }) </script> </head> <body> </body> </html>
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script> <script type="text/javascript"> $(function() { function runAsync() { var def = $.Deferred(); setTimeout(function() { console.log('執行完成'); def.resolve('隨便什麼數據'); }, 2000); return def.promise(); } var pms=runAsync(); pms.then(function(data) { console.log(data) }); //pms.resolve('我穿越了!'); //這一句會報錯jquery-3.1.1.min.js:2 Uncaught TypeError: pms.resolve is not a function }) </script> </head> <body> </body> </html>
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script> <script type="text/javascript"> $(function() { function runAsync() { var def = $.Deferred(); setTimeout(function() { console.log('執行完成'); def.resolve('隨便什麼數據'); }, 2000); return def.promise(); } var pms = runAsync(); pms.then(function(data) { console.log('1:' + data); return runAsync(); }) .then(function(data) { console.log('2:' + data); return runAsync(); }) .then(function(data) { console.log('3:' + data); }); //pms.resolve('我穿越了!'); //這一句會報錯jquery-3.1.1.min.js:2 Uncaught TypeError: pms.resolve is not a function }) </script> </head> <body> </body> </html>
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
d.then(function(){ console.log('執行完成'); }, function(){ console.log('執行失敗'); });
d.done(function(){ console.log('執行完成'); }) .fail(function(){ console.log('執行失敗'); });
$.when(runAsync(), runAsync2(), runAsync3()) .then(function(data1, data2, data3){ console.log('所有執行完成'); console.log(data1, data2, data3); });
req1 = function(){ return $.ajax(/*...*/); } req2 = function(){ return $.ajax(/*...*/); } req3 = function(){ return $.ajax(/*...*/); } req1().then(req2).then(req3).done(function(){ console.log('請求發送完畢'); });
$.ajax(/*...*/) .success(function(){/*...*/}) .error(function(){/*...*/}) .complete(function(){/*...*/})
deferred.promise( jqXHR ).complete = completeDeferred.add; jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail;
/*! * Promise JavaScript Library v2.0.0 */ ; (function(window) { var _promise = function(thens) { this.thens = thens || []; this.state = ""; this._CONSTANT = { any: "any", number: "number", resolved: "resolved", rejected: "rejected", pending: "pending" }; }; _promise.prototype = { resolve: function() { if(this.state == this._CONSTANT.pending) { this.state = this._CONSTANT.resolved; return; } if(this.state !== "") return; if(this.promiseArr) { for(var i = 0, j = this.promiseArr.length; i < j; i++) { this.promiseArr[i].resolveCount++; } if(this.promiseArr[0].action !== this._CONSTANT.any) { if(this.resolveCount !== this.promiseArr.length) { return; } } else { if(this.resolveCount > 1) { return; } } } this.state = this._CONSTANT.resolved; if(!this.thens) return; if(this.thens[0] && this.thens[0].finallyCB) this.thens[0].finallyCB.apply(null, arguments); var t, n; while(t = this.thens.shift()) { if(typeof t === this._CONSTANT.number) { var self = this; setTimeout(function() { var prms = new _promise(self.thens); prms.resolve(); }, t); break; } var doneFn = t.done, action = t.action; if(!doneFn) continue; if(doneFn instanceof Array) { var arr = []; for(var i = 0, j = doneFn.length; i < j; i++) { var df = doneFn[i]; if(df instanceof _promise) { df.thens = this.thens; arr.push(df); } else { var m = df.apply(null, arguments); if(m instanceof _promise) { m.thens = this.thens; arr.push(m); } } } var l = arr.length; if(l === 0) { continue; } else { for(var i = 0; i < l; i++) { arr[i].promiseArr = arr; arr[i].action = action; arr[i].resolveCount = 0; } break; } } else { if(doneFn instanceof _promise) { doneFn.thens = this.thens; break; } else { n = doneFn.apply(null, arguments); if(n instanceof _promise) { n.thens = this.thens; break; } } continue; } } }, reject: function() { if(this.state !== "") return; if(this.promiseArr && this.promiseArr[0].action === this._CONSTANT.any) { if(this.promiseArr[this.promiseArr.length - 1] !== this) { return; } } this.state = this._CONSTANT.rejected; if(!this.thens) return; if(this.thens[0] && this.thens[0].finallyCB) this.thens[0].finallyCB.apply(null, arguments); var t, n; while(t = this.thens.shift()) { if(typeof t === this._CONSTANT.number) { var self = this; setTimeout(function() { var prms = new _promise(self.thens); prms.resolve(); }, t); break; } if(t.fail) { n = t.fail.apply(null, arguments); if(n instanceof _promise) { n.thens = this.thens; break; } continue; } break; } }, notify: function() { var t = this.thens[0]; t.progress.apply(null, arguments); }, then: function(done, fail, progress) { this.thens.push({ done: done, fail: fail, progress: progress }); return this; }, any: function(done, fail, progress) { this.thens.push({ done: done, fail: fail, progress: progress, action: this._CONSTANT.any }); return this; }, done: function(done) { if(this.thens.length === 0 || this.thens[this.thens.length - 1].done) { this.thens.push({ done: done }); } else { this.thens[this.thens.length - 1].done = done; } return this; }, fail: function(fail) { if(this.thens.length === 0 || this.thens[this.thens.length - 1].fail) { this.thens.push({ fail: fail }); } else { this.thens[this.thens.length - 1].fail = fail; } return this; }, progress: function(progress) { if(this.thens.length === 0 || this.thens[this.thens.length - 1].progress) { this.thens.push({ progress: progress }); } else { this.thens[this.thens.length - 1].progress = progress; } return this; }, ensure: function(finallyCB) { if(this.thens.length === 0 || this.thens[this.thens.length - 1].finallyCB) { this.thens.push({ finallyCB: finallyCB }); } else { this.thens[this.thens.length - 1].finallyCB = finallyCB; } return this; }, always: function(alwaysCB, progress) { this.thens.push({ done: alwaysCB, fail: alwaysCB, progress: progress }); return this; }, wait: function(ms) { this.thens.push(~~ms); return this; } } var Promise = function(parameter) { var prms = new _promise(); if(parameter) { if(arguments.length > 1) { prms.thens[0] = {}; prms.thens[0].done = []; prms.thens[0].done.push.apply(prms.thens[0].done, arguments); setTimeout(function() { prms.resolve(); }, 1) } else { prms = parameter(); if(prms instanceof _promise) return prms; } } return prms; }; Promise.when = function() { var prms = new _promise(); prms.thens[0] = {}; prms.thens[0].done = []; prms.thens[0].done.push.apply(prms.thens[0].done, arguments); setTimeout(function() { prms.resolve(); }, 1) return prms; }; Promise.any = function() { var prms = new _promise(); prms.thens[0] = {}; prms.thens[0].action = prms._CONSTANT.any; prms.thens[0].done = []; prms.thens[0].done.push.apply(prms.thens[0].done, arguments); setTimeout(function() { prms.resolve(); }, 1) return prms; }; Promise.timeout = function(promise, ms) { setTimeout(function() { promise.reject(); }, ms); return promise; } Promise.gtTime = function(promise, ms) { promise.state = promise._CONSTANT.pending; setTimeout(function() { if(promise.state == promise._CONSTANT.resolved) { promise.state = ""; promise.resolve(); } promise.state = ""; }, ms); return promise; } if(typeof module === "object" && module && typeof module.exports === "object") { module.exports = Promise; } else { window.Promise = Promise; if(typeof define === "function" && define.amd) { define("promise", [], function() { return Promise; }); } } }(window));
promise.js提供了done和resolve方法,done負責註冊成功的回調函數,resolve負責觸發。
function cb() {
alert('success') } var prms = Promise() prms.done(cb) setTimeout(function() { prms.resolve() }, 3000)
在3秒以後,瀏覽器將alert 「success」。
固然你也能夠經過prms.resolve(「xxx」)傳遞參數給cb函數使用,如:
function cb(num) {
alert(num)
}
var prms = Promise() prms.done(cb) setTimeout(function() { prms.resolve(1) }, 3000)
在3秒以後,瀏覽器將alert 「1」。
fail函數負責註冊失敗的回調函數,reject負責觸發。如:
function cb() {
alert('fail') } var prms = Promise() prms.fail(cb) setTimeout(function () { prms.reject() }, 3000)
progress函數負責註冊處理中進度的回調函數,notify負責觸法。如:
function cb() {
alert('progress') } var prms = Promise() prms.progress(cb) setInterval(function() { prms.notify() }, 2000)
每隔兩秒瀏覽器會彈出一個progress。
function cb1() {
alert('success') } function cb2() { alert('fail') } function cb3() { alert('progress') } var prms = Promise(); prms.done(cb1).fail(cb2).progress(cb3) setTimeout(function () { prms.resolve() //prms.reject() //prms.notify() }, 3000)
function fn1() {
alert('success') } function fn2() { alert('fail') } function fn3() { alert('progress') } var prms = Promise() prms.then(fn1, fn2, fn3) prms.resolve() prms.reject() prms.notify()
固然也支持prms.then().then().then()……….
當then的第一個參數爲一個數組的時候,要等全部task都完成:
f1().then([f2_1, f2_2]).then(f3)
如上面的代碼:
f1執行完後,同時執行f2_1和f2_2,當f2_1和f2_2所有都執行完成纔會執行f3。
f1().any([f2_1, f2_2]).then(f3)
f1執行完後,同時執行f2_1和f2_2,當f2_1和f2_2中的任意一個執行完成纔會執行f3。
var prms = Promise()
prms.always(function () { alert(2) }) setTimeout(function () { // prms.resolve() prms.reject() }, 3000)
always(fn)等同於then(fn,fn),也等同於done(fn).fail(fn)
function f10() {
var promise = Promise(); setTimeout(function () { console.log(10); promise.resolve(); }, 4500) return promise; } function f11() { var promise = Promise(); setTimeout(function () { console.log(11); promise.resolve(); }, 1500) return promise; } f11().wait(5000).then(f10) //execute f11 then wait 5000ms then execute f10
ensure方法相似try…catch..finally中的finally,無論task成功失敗都會執行。
Promise.when(f1(), f2()).then(f3).then(f4)
function f1() {
var promise = Promise(); setTimeout(function () { console.log(1); promise.resolve("from f1"); }, 1500) return promise; } function f2() { var promise = Promise(); setTimeout(function () { console.log(2); promise.resolve(); }, 5500) return promise; } function f3() { var promise = Promise(); setTimeout(function () { console.log(3); promise.resolve(); }, 1500) return promise; } function f4() { var promise = Promise(); setTimeout(function () { console.log(4); promise.resolve(); }, 1500) return promise; }
上面promise.when的等同簡略寫法也能夠是:Promise(f1(),f2()).then….
Promise.any的使用和when同樣,when的意義是等全部task都完成再執行後面的task,而any的意義是任何一個task完成就開始執行後面的task。
Promise.timeout(f1(), 2000).then(f2, function () {
alert("timeout"); }).wait(5000).then(f3); function f1() { var promise = Promise(); setTimeout(function () { console.log(1); promise.resolve("from f1"); }, 1500) return promise; } function f2() { var promise = Promise(); setTimeout(function () { console.log(2); promise.resolve(); }, 1500) return promise; } function f3() { var promise = Promise(); setTimeout(function () { console.log(3); promise.resolve(); }, 1500) return promise; }
<script src="wind-all-0.7.3.js"></script> <script src="promise.js"></script> <script> Wind.Promise.create = function (fn) { var prms = Promise(); fn(prms.resolve, prms.reject); return prms; } var testAsync = eval(Wind.compile("promise", function () { for (var i = 0; i < 3; i++) { //loop 3 times var aa = $await(f1()); alert(aa); //alert 「from f1」 $await(f2().wait(3000)); //execute f2 then wait 3000ms $await(f3()); } })); testAsync(); function f1() { var promise = Promise(); setTimeout(function () { console.log(1); promise.resolve("from f1"); }, 2500) return promise; } function f2() { var promise = Promise(); setTimeout(function () { console.log(2); promise.resolve(); }, 1500) return promise; } function f3() { var promise = Promise(); setTimeout(function () { console.log(3); promise.resolve(); }, 1500) return promise; } </script>
That’s all.Have Fun!
不少文章介紹Promise給的例子是這樣的:
new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open('POST', location.href, true); xhr.send(null); xhr.addEventListener('readystatechange', function(e){ if(xhr.readyState === 4) { if(xhr.status === 200) { resolve(xhr.responseText); } else { reject(xhr); } } }) }).then(function(txt){ console.log(); })
必定會有小朋友好奇,說尼瑪,這不是比回調還噁心?
這種寫法的確是能跑得起來啦……不過,按照Promise的設計初衷,咱們編程須要使用的概念並不是"Promise對象",而是promise函數,凡是以Promise做爲返回值的函數,稱爲promise函數(我暫且取了這個名字)。因此應該是這樣的:
function doSth() { return new Promise(function(resolve, reject) { //作點什麼異步的事情 //結束的時候調用 resolve,好比: setTimeout(function(){ resolve(); //這裏纔是真的返回 },1000) }) }
若是你不喜歡這樣的寫法,還可使用defer風格的promise
function doSth2() { var defer = Promise.defer(); //作點什麼異步的事情 //結束的時候調用 defer.resolve,好比: setTimeout(function(){ defer.resolve(); //這裏纔是真的返回 },1000) return defer.promise; }
總之兩種是沒什麼區別啦。
而後你就能夠這麼幹:
doSth().then(doSth2).then(doSth);
這樣看起來就順眼多了吧。
其實說簡單點,promise最大的意義在於把嵌套的回調變成了鏈式調用(詳見第三節,順序執行),好比如下
//回調風格 loadImage(img1,function(){ loadImage(img2,function(){ loadImage(img3,function(){ }); }); }); //promise風格 Promise.resolve().then(function(){ return loadImage(img1); }).then(function(){ return loadImage(img2); }).then(function(){ return loadImage(img3); });
後者嵌套關係更少,在多數人眼中會更易於維護一些。
在去完cssconf回杭州的火車上,我順手把一些常見的JS和API寫成了promise方式:
function get(uri){ return http(uri, 'GET', null); } function post(uri,data){ if(typeof data === 'object' && !(data instanceof String || (FormData && data instanceof FormData))) { var params = []; for(var p in data) { if(data[p] instanceof Array) { for(var i = 0; i < data[p].length; i++) { params.push(encodeURIComponent(p) + '[]=' + encodeURIComponent(data[p][i])); } } else { params.push(encodeURIComponent(p) + '=' + encodeURIComponent(data[p])); } } data = params.join('&'); } return http(uri, 'POST', data || null, { "Content-type":"application/x-www-form-urlencoded" }); } function http(uri,method,data,headers){ return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.