关于ES6中的promise

前言

之前看到一道题目,假设我们知道了红绿灯的函数,然后让他们间隔,3秒,2秒,1秒循环闪烁~
因为之前遇到的间隔问题都是等间隔。。突然一个不规律的周期有点懵。然后看到这个可以用promies实现,遂,来补一补promise的知识~(2016年了!AJAX手写也要换ES6嘛~)

promise

promise是ES6标准里新添加的一个对象,他可以用来生成promise实例。
promise接收一个函数作为参数,这个函数的两个参数分别是resolve,reject。前者是约定对象状态由pending(未定)到成功时调用的,并将异步调用结果用参数传递出去~然后reject是约定的对象状态由未定变成失败时,也就是异步调用失败时调用的,错误作为参数传递出去。使用的时候我们用promise.then(function(value){
成功},function(error){失败})这里的value和error就是之前传递过来的啦~!
举个简单的例子:

function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
 console.log(value);
});

我们返回了一个promise实例。表示一段时间之后要做的事情。过了指定的时间后,promise状态变为resolved就触发了then绑定的回调函数。
那么我们再看一个进阶的例子~(手写AJAX)

var getJSON = function(url) {
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();

function handler() {
  if (this.readyState !== 4) {
    return;
  }
  if (this.status === 200) {
    resolve(this.response);
  } else {
    reject(new Error(this.statusText));
  }
};
});
return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});

是不是无比熟悉(是的)。稍微解释一下~getJSON是XHR对象的一个封装,定义了请求的方法get,请求的URL:url。相应的文件类型json,请求头,已经发送命令~定义了onreadystatechange的回调函数~就是经典的readyState不为4,返回空,状态码为200时,调用resolve把异步请求的值传递给then的回调函数。失败的话,定义一个new Error()把错误信息传递进去。
最后返回我们的promise对象。
之后就是调用then啦~成功的话执行回调函数使用请求回的信息。失败的话,打印出错误~
到这里promise大概是个什么回事也久清除了~promise还有很多其他的API。
catch:错误时的回调函数,catch(function(error){出错})
如果嵌套了promise的话,catch能够捕捉到所有的错误内容。一般来说使用then(,function(error){})的第二个函数,也是可以的。但是最好使用catch来执行发生错误的回调函数。promise.then().catch()。
关于promise的执行状态这里用一样图来描述吧~

题目解答

这里就解答一下开头提到的红绿灯题目吧~
不同的间隔,然后循环,首先把不同的间隔的函数写出来~这里我们也可以用promise来写,直接不传递参数给resolve,使得异步必定成功,然后执行回调函数~

var tic = function( delayTime , light ){
          return new Promise(function(resolve,reject){
                                 setTimeout(
                                         function(){
                                               light();
                                                resolve();
                                           }
                              , delayTime)
                 });
              };

这个函数返回一个promise,也是一个延迟的函数时间和亮灯函数自己来设置。
然后是循环函数

var d = new Promise(function(resolve,reject){resolve();})
var step = function(def){
         def.then(function(){
                    return tic(3000,red);
         }).then(function(){
                    return tic(2000,green);
         }).then(function(){
                    return tic(1000,yellow);
         })
}

OK接下来就是添加循环了~!切忌不能

while(1){
   /*我们的周期函数*/  
}    

then是异步调用,我们这样子写的话,主进程会一直的while循环,我们的setTImeout永远不会执行。
正确的姿势是~:递归!
在循环体的最后再次调用循环函数.then(function(){step(def)});
OK!

promise事件队列

那么为什么会这样子去进行循环呢?
then和setTimeout实际上是在event loop中两个不同的队列里的!setTime在宏观队列(macrotask)then在微观队列(microtack)中。JS会先执行宏观队列中的代码,第一个任务结束后,执行所有的微观队列,然后再到宏观队列,依次往复~队列中事件的推入是执行第一个macrotack时,也就是script脚本时,决定的。
所以这题的事件循环可以表现为:
执行 step函数;
d返回的promise中处理script,完毕后进入微观队列,处理第一个then,返回第二个promise对象,第二个promise对象中注册了宏观队列中的setTimeout,执行后,返回了peomise成功,然后执行then,然后后面的都是重复~
这里的循环其实就是从主任务入口开始,进入第一个then即微观队列,然后遇到setTimeout,由于没有生成promise的resolve还没有运行,接下来的then需要等到,我们会注册延迟函数到宏观队列,然后去执行宏观队列内容,然后执行完毕后再次执行微观队列,周而复始!
记住!永远会宏观执行完毕后,执行微观的全部,然后再次执行宏观下一个任务。

结语

大概对promise的理解就是这样了~下一篇- -!把flex总结一下吧!