在前端的平常工做中,回调函数(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
举个🌰:bash
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){ //干正事啦! }) 复制代码
确实。简洁了很多,至少不会被里三层外三层的括号弄晕。markdown
可是当初我听到promise的时候,我心里是拒绝的。虽然心里拒绝,可是该来的仍是要来的,该学的仍是要学的,毕竟时代在进步,与时俱进仍是很必要的!那么这个promise是怎么实现的呢???异步
小伙伴们,这里promise可不是男女约会中浪漫的台词 ”I promise XXX“ ,而是一种规范,点击此处获取规范。不过这里的promise和现实生活中的promise同样,都有实现(fulfilled),拒绝(rejected)和等待(pending)这三种状态。async
举个🌰:函数
假定 Mary 和 Mike 是一对情侣,半年前,Mike 向 Mary 承诺(promise)半年内完成他们的婚礼,可是直到如今 Mike 也没有作出行动,所以 Mary 表示她不会一直等待(pending)下去,因而他们分手了,那么这个承诺(promise)就是做废了(rejected)。若是这半年内 Mike 和 Mary 结了婚,那么如今 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
复制代码