• 首页
  • javascript
  • promise规范 让 javascript 中的异步调用更加人性化

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函数的对象即可.


回到顶部