promise规范 让 javascript 中的异步调用更加人性化
Promise是commonjs规范中的内容.
现已经纳入了ES6,并且高版本的chrome、firefox浏览器都已经原生实现了Promise,只不过和现如今流行的类Promise类库相比少些API.
这里说的是指 Promise/A+ 规范.
Promose 主要用于异步调用规范,
它有三种状态, 等待(pending)、已完成(fulfilled)、已拒绝(rejected)
等待 --> 已完成 / 等待 --> 已拒绝 但是已完成和已拒绝状态不可能互相转化.
为什么要出现 Promose 规范:
一直以来,对于javascript的异步回调,我们祖祖辈辈都是用callback方式 (貌似也没有多少年)
比如:
我从学校服务器需要获取班级A的所有同学列表,
然后从同学列表中找到成绩前3名学生信息,
然后同时提交3名学生信息到XX教育局网站申请奖学金(世界多么的美好^_^).
getTop3Students(schoolID,function(studentsList){ //获取班级列表前3名同学列表信息 var submitStudentList=[]; var tmpIndex=studentsList.length; for(var i=0;i<studentsList.length;i++){ getStudentDetail(studentsList[i].studentID,function(studentInfo){ //根据学生ID获取学生详细信息 studentInfo&&submitStudentList.push(studentInfo); //把学生信息提交到submitStudentList数组 }); } if(--tmpIndex===0) //计数器,如果获取到列表中的所有学生详细信息后 postStudentsInfo(submitStudentList,function(result){ //提交到教育局申请奖学金,哈哈 return result; }); });
上面是随便演绎的一段程序,没有定义方法的地方,所以运行报错哦.只是举例而已.
我们可以看到所以的操作都是嵌套在上一个业务逻辑的回调函数中.
如果业务逻辑比较复杂,嵌套10几层,几十层都不是问题,这样写代码,你想过维护代码吗?
所以Promose 规范横空出世,帮你解决异步回调的噩梦,让你从深层嵌套callback函数中解脱出来.
正如它的名字一样,它给了我们一个"承诺"
如何理解这个承诺?
为了说清楚这个词,我决定带大家再意淫一把(想起了那句话,带你装B带你飞....好恰当).
博客广告太弱,维持不了vps租金,所以一介布衣打算让阿里云赞助一下,如果成功就撤掉广告,如果失败.....继续交vps费用
当我向阿里云提出是否可以赞助的时候,这就是一个Promose 过程
因为当我提交这个意愿后,其实我心里需要阿里云给我一个承诺: (这个过程是pending ,可以转换为 fulfilled 或者 rejected)
承诺内容:
同意了 ( pending --> fulfilled) 一介布衣做出响应: 撤下广告
拒绝了 ( pending --> rejected) 一介布衣做出响应: 马上交vps费用
但是状态从 pending 到 fulfilled/rejected 转换过程中,阿里云该干啥干啥,一介布衣想干啥干啥.
这就是 Promose 的规范的意义.你给我做出一个承诺,我根据承诺不同的结果给出不同的响应,但是在执行期间,我们互不影响,互不等待,它办理它的业务,我写我的博客.
对于调用者来说,这种承诺更像是一种预知,然后根据预知的不同结果给出不同的处理方法.代码效果和上面一样,就不写了.....
Promose 实例:
既然是个规范,我们谁都可以按照此规范去定义Promose 类,目前开源的Promose 流程控制类已经非常多,注意的是 jquery 中的deferred 实现并不是严格按照commonjs 规范的 Promose实现.虽然 deferred 对象也可以转化成 promose 对象,但是在转化过程中只提取了rejected,fulfilled函数中的第一个返回值,其余返回值全部丢弃.
function Promise(resolver) { resolver(resove, reject) }
一个最简单的 Promose 实现.可以看出每次调用后需要返回一个 Promose 对象,所以导致它可以链式调用.
但是没有then函数,我们还是不能链式去then调用,接着如下修改:
function Promise(resolver) { resolver(resolve, reject) var queue = [] // 保存链式调用的数组 this.then = function(resolve, reject) { queue.push([resolve, reject]) // 把then中的resolve和reject都存起来 } } function resolve(x) { next(0, x) // 简单的用0来告诉next是resolve } function reject(resson) { next(1, reason) // 同理用1告诉next是reject } function next(i, val) { // i 仅仅是用来区分resolve还是reject, val是值 while (queue.length) { var arr = queue.shift() // 移出一个resolve和reject对, 也就是[resolve, reject] arr[i](val) // 执行之, val是唯一参数 } }
定义好以后,如何调用呢
var p = new Promise(function(resolve) { resolve('ok') }) p.then(function(x) { console.log(x) })
因为完全没有延迟, 显然resolve先走了, 而resolve执行的时候, queue中还没有函数去接它, 这个时候就then就不可能触发了
因此要么把resolve的值存起来, 要么就是让resolve肯定晚于后面的then执行
我们用一下setTimeout 来模拟延迟
function(i, val) { setTimeout(function() { while (queue.length) { var arr = queue.shift() arr[i](val) } }) }
上面的函数如何实现链式调用呢,
一般来说链式的话resolve返回值为一个promise对象
所谓promise对象, 其实不过是 {then: function() {} }
也就是一个含有then函数的对象即可.