在前端的平常工做中,回調函數(callback)應該是見怪不怪了,可是當回調函數趕上了異步(async),這就使人髮指了。那麼異步是什麼意思呢,簡單地說就是不等你執行完,就先執行下方的代碼了。前端
舉個🌰:git
咱們最經常使用的異步操做應該是ajax了(想當初我第一次用ajax的時候,簡直就是災難。明明資源加載成功了,怎麼就是沒有調到資源中的數據呢?真使人頭大啊。),只能等待加載完畢,再執行相關操做才能成功。所以咱們看到的代碼應該都是這樣的。github
/** @param callback 回調函數 */ function getData(url,callback){ $.ajax({ url:url, success:function(result){ callback(result); } }); } //假設我有好多個ajax,每一個ajax都須要上一個ajax的支持,因而……地獄出現了…… getData(url1,function(res){ getData(url2,function(res){ getData(url3,function(res){ //終於能夠幹正事了 }) }) })
朋友們,回調地獄(callback Hell)瞭解下。ajax
因而promise出現了,他的出現就是解決了回調地獄!他對異步的函數進行了封裝,把回調變成了鏈式調用。promise
舉個🌰:異步
function getData(url){ return new Promise((resolve,reject)=>{ $.ajax({ url:url, success:function(result){ resolve(result); }, error:function(error){ reject(error); } }); }) } getData(url1).then(function(res){ return getData(url2) }).then(function(res){ return getData(url3) }).then(function(res){ //幹正事啦! })
確實。簡介了很多,至少不會被裏三層外三層的括號弄暈。async
可是當初我聽到promise的時候,我心裏是拒絕的。雖然心裏拒絕,可是該來的仍是要來的,該學的仍是要學的,畢竟時代在進步,與時俱進仍是很必要的!那麼這個promise是怎麼實現的呢???函數
小夥伴們,這裏promise可不是男女約會中浪漫的臺詞 」I promise XXX「 ,而是一種規範,點擊此處獲取規範。不過這裏的promise和現實生活中的promise同樣,都有實現(fulfilled),拒絕(rejected)和等待(pending)這三種狀態。性能
舉個🌰:this
假定 Mary 和 Mike 是一對情侶,半年前,Mike 向 Mary 承諾(promise)半年內完成他們的婚禮,可是直到如今 Mike 也沒有作出行動,所以 Mary 表示她不會一直等待(pending)下去,因而他們分手了,那麼這個承諾(promise)就是做廢了(rejected)。若是這半年內 Mike 結了婚,那麼如今 Mike 應該已經實現(fulfilled)了他對 Mary 的承諾(promise)。
因此說,全部的promise都有一個結果狀態——實現(fulfilled)或者拒絕(rejected),而結果出來以前的狀態就是等待(pending)。
//p1.js function Promise(executor){ let _=this; _.value=undefined; _.reason=undefined; _.state="pending"//你們一開始都是同樣,等着吧 function resolve(value){ _.value=value//實現以後的感言 _.state="fulfilled"//實現啦! } function reject(reason){ _.reason=reason //給我一個被拒絕的理由 _.state="rejected" //被拒絕了! } executor(resolve,reject) } //e.g let Iagree=new Promise((resolve,reject)=>{ resolve("我開心就贊成了");// }) let Idisagree=new Promise((resolve,reject)=>{ reject("我不開心就拒絕了"); }) let noResult=new Promise((resolve,reject)=>{ }) console.log(Iagree.state,Idisagree.state,noResult.state)
不過我只知道一個狀態有何用?我還要進行下一步噠!咱們須要一個then
,用於進行下一步的操做。
//p2.js Promise.prototype.then=function(onFulfilled, onRejected){ let _=this; if(_.state=="pending"){} if(_.state=="fulfilled"){ onFulfilled(_.value) } if(_.state=="rejected"){ onRejected(_.reason) } } //e.g let Iagree=new Promise((resolve,reject)=>{ resolve("我開心就贊成了");//強行完成(fullfilled) }) Iagree.then((data)=>{ console.log(Iagree.state) },(e)=>{ console.log(e) })
不過這個都是同時進行,不是異步的。咱們來瞅一眼異步~
這個時候咱們須要把回調函數丟到resolve或者reject中,可是若是咱們的後續方法不少呢?then好屢次怎麼辦!將回調丟到的隊列中,到時候Foreach一下逐個執行。
//p3.js function Promise(executor){ //.... _.resolveCallbacks=[];//callbacks在pending中添加,fullfilled中執行 _.rejectCallbacks=[];//callbacks在pending中添加,rejected中執行 function resolve(value){ //.... _.resolveCallbacks.forEach((fn)=>fn()) } function reject(reason){ //.... _.rejectCallbacks.forEach((fn)=>fn()) } //.... } Promise.prototype.then=function(onFulfilled, onRejected){ let _=this; if(_.state=="pending"){ //把回調方法塞進隊列中 _.resolveCallbacks.push(()=>{ onFulfilled(_.value) }) _.rejectCallbacks.push(()=>{ onRejected(_.reason) }) } //.... } //e.g let Iagree=new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve("我開心就贊成了"); },1000) }) //爲了防止屢次then,因此回調方法須要丟入隊列中,防止方法被覆蓋。 Iagree.then((data)=>{ console.log(Iagree.state) },(e)=>{ console.log(e) }) Iagree.then((data)=>{ console.log(Iagree.state+1) },(e)=>{ console.log(e) })
那麼問題來了,若是我直接then,可不能夠?像這這樣:
Iagree.then((data)=>{ ... }).then((data)=>{ ... }).then((data)=>{ ... })
若是想要這樣寫,那麼上一步的then
必須返回一個promise對象才能夠,否則哪裏變出一個then
方法。所以咱們須要在then
中new
一個新的promise,用於下一個鏈式調用的then
。
//p4.js function resolvePromise(promise,x,resolve,reject){ //若是x多是一個promise if(x!==null&&(typeof x==="object"||typeof x==="function")){ let then=x.then; //若是x是一個promise,由於promise都要有then函數的 if(typeof then === "function"){ //y表示x這個promise的值 then.call(x,y=>{ //繼續遍歷,直至返回值不是promise resolvePromise(promise,y,resolve,reject) },err=>{ reject(err) }) }else{ //若是x是個普通對象,直接運行 resolve(x) } }else{ //若是x不是一個promise,也就是x是一個常量,直接運行 resolve(x) } } Promise.prototype.then=function(onFulfilled, onRejected){ let _=this; let promise2; //將當前promise的值傳遞到下一次then的調用中 function resolveFunction(promise,resolve,reject){ let x=onFulfilled(_.value) resolvePromise(promise,x,resolve,reject) } function rejectFunction(promise,resolve,reject){ let x=onRejected(_.reason) resolvePromise(promise,x,resolve,reject) } promise2=new Promise((resolve,reject)=>{ if(_.state=="pending"){ //把回調方法塞進隊列中 _.resolveCallbacks.push(()=>{ resolveFunction(promise2,resolve,reject) }) _.rejectCallbacks.push(()=>{ rejectFunction(promise2,resolve,reject) }) } if(_.state=="fulfilled"){ resolveFunction(promise2,resolve,reject) } if(_.state=="rejected"){ rejectFunction(promise2,resolve,reject) } }) return promise2 } //e.g let Iagree=new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve("我開心就贊成了"); },1000) }) //爲了防止屢次then,因此回調方法須要丟入隊列中,防止方法被覆蓋。 Iagree.then((data)=>{ console.log(data) return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve("看心情幹活"); },1000) }) }).then((data)=>{ console.log("前方返回一個promise:"+data) return data+",我是一個常量" }).then((data)=>{ console.log("常量返回:"+data) }).then((data)=>{ console.log("前方沒法返回:"+data) })
這樣咱們就能夠愉快地用鏈式調用promise了,想一想就美滋滋。
不過以上只是簡單粗暴的實現promise的方式,只是一個原理,還有promise的一些規範須要完善點擊此處獲取規範。
總結幾點
try{}catch(){}
的地方都標記上,寧肯錯殺不放過。setTimeout
之中,爲了讓他們變成「宏任務(macro-task)」。(應該是出於性能的考慮,以後再研究。)Promise.defer = Promise.deferred = function(){}
方法,防止篡改。module.exports=Promise
。promises-aplus-tests.cmd 你的promise.js
,而後一行行地檢查你的代碼,等到所有變綠(passing),恭喜你成功攻克promise!!//參考p5.js