Promise

作者: kim_jin | 来源:发表于2019-06-03 11:49 被阅读0次

简介

Promise是一种异步编程的解决方案,比传统的解决方案-- 回调行数和事件更加的合理和强大,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。而Promise出现主要是因为现阶段异步请求和数据之间存在依赖关系,如果使用传统的方法会产生很多难看的多层回调,这样的代码不利于进行阅读和后期的维护,俗称“回调地狱”,而且我们将错误处理代码和业务代码耦合在一起,造成代码的可读性变差,这个时候我们就引入Promise来降低异步编程的复杂性。
那么究竟什么是Promise呢?简单来说Promise就是一个容器,里面保存了某一个未来才会结束的事件(通常是一个异步操作)的结果,在语法的意义上面,Promise是一个对象,从他可以获取异步操作的消息。
Promise的特点:

  • 对象的状态是不受外界的影响的,Promise对象代表一个异步的操作。有三个状态:pendingfulfilledreject。只有异步操作的结果,可以决定当前是哪一种状态。这也是Promise名字的由来,Promise是承诺的意思,意味着其他的手段是无法改变的。
  • 一旦状态发生改变就不会在进行改变了。任何时候都是这个结果,Promise对象的状态改变,只有两种情况pending变成fulfilled或是pending变成reject,只要状态发生改变了,状态发生改变了,状态就会凝固,不会在发生改变。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

Promise基本语法

  • Promise实例必须实现then这个方法
  • then()必须可以接收两个函数作为参数(第二个参数是可选的)

Promise基础用法

Promise的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。形如这种形式:

f1().then(f2);

我们可以看到上面的写法,将回调函数变成了链式的写法,城西的流程可以很清晰的看出来,而且有一套配套的方法,这样的话可以实现很多强大的功能。
我们看一下Promise的实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调行数

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

再举一个制定多个回调函数的例子,其形式为:

f1().then(f2).then(f3);

下面我们就从一个例子进行入手,看一下上面的实现究竟是怎么样的:

runAsync1 = () =>{
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务1执行完成');
            resolve('随便什么数据1');
        }, 1000);
    });
    return p;            
}
runAsync2 = () =>{
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务2执行完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    return p;            
}
runAsync3 = () =>{
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务3执行完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});

上面的例子我们得到的结果是

结果

结果的输出:每隔两秒输出每个异步回调中的内容,在runAsync2中传给resolve的数据,能在接下来的then方法中拿到,那么如果我们返回的不是Promise对象呢,而是返回数据呢,我们将代码进行修改:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return '直接返回数据';  //这里直接返回数据
})
.then(function(data){
    console.log(data);
});

打出的结果就是:

结果

当我们要处理有reject的时候,我们使用的模板是:

f1().then(f2(),f3());

上面的代码是在f1()的状态由pending变成fulfilled的时候执行f2()函数,在pending编程rejected的时候执行f3()函数。
我们之前一直使用promise来处理ajax的请求,我们通过代码看一下:

const promise = new Promise((resolve, reject) => {
  $.ajax('https://github.com/users', (value) =>  {
    resolve(value);
  }).fail((err) => {
    reject(err);
  });
});
promise.then((value) => {
  console.log(value);
},(err) => {
  console.log(err);
});

在上面的例子中,会在ajax请求成功后调用resolve回调函数来处理结果,如果请求失败就调用reject糊掉行数来处理错误,Promise对象一般包含三个状态,分别是pending,fulfilledrejected,对应ajax中的pending,success,error。成功获得值后状态就变为fulfilled,然后将成功获取到的值存储起来,后续可以通过调用then方法传入的回调函数来进一步处理。而如果失败了的话,状态变为rejected,错误可以选择抛出(throw)或者调用reject方法来处理。
then 方法可以被同一个 promise 调用多次,then方法必须返回一个promise 对象。`

promise的其他用法

promise.catch()

promise.catch() <==>.then(null, rejection).then(undefined, rejection)
这个方法用来指定错误发生时的回调函数,其实它和then的第二个参数一样,用来指定reject的回调

getNumber()
.then((data)=>{
    console.log('resolved');
    console.log(data); // 如果这里出错了,也会进入到catch函数中
})
.catch((reason)=>{
    console.log('rejected');
    console.log(reason);
});

效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中

promise.all()

其实all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,也就是最慢的一个执行完成,才会执行回调函数。
Promise.all方法用于将多个Promise实例,包装成一个新的Promise 实例。这个封装的方法提供了并行执行异步操作的能力,并且所有的异步操作都执行完成后才执行回调行数。我们还是使用上面的runAsync1runAsync2runAsync3的方法测试一下:

Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。所以上面代码的输出结果就是:

结果

Promise.all接受一个promise对象的数组,待全部完成之后,统一执行success;

promise.race()

「谁跑的快,以谁为准执行回调」,这就是race方法,也就是我们如果使用了race的方法,一旦有执行完成的,就会执行回调行数,那我们继续举一个例子来看一下,现在我们将runAsync1方法的延时修改成1s来看一下:

Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

这个时候我们打出的结果是:

结果

then里面的回调开始执行时,runAsync2()runAsync3()并没有停止,仍旧再执行。于是再过1秒后,输出了他们结束的标志。
romise.race接受一个包含多个promise对象的数组,只要有一个完成,就执行success。

promise.finally()

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。也就是finally的方法是与状态无关的

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});  // 这个函数是一定会被执行的

finally的实现原理是:

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

也就是返回了原来的值:

// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

Promise的resolve和reject和catch(当状态码为404,5XX的时候会进入到reject中进行执行)

function load(){

   var xmlhttp;

   if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera,Safari

      xmlhttp= new XMLHttpRequest();

   }else{// code for IE6, IE5

      xmlhttp= new ActiveXObject("Microsoft.XMLHTTP");

   } 

   xmlhttp.onreadystatechange= function(){

      if (xmlhttp.readyState ==4 && xmlhttp.status == 200) {//获得了请求数据

 var expertinfolist = xmlhttp.responseText;

   //发送请求数据到myDiv     document.getElementById("myDiv").innerHTML=expertinfolist;

      }

   }

   var url="expert_ZFTservlet?expert_name="+"曾攀";

   xmlhttp.open("GET", '/js/jquery-ui.min.js', true);

   xmlhttp.send();

}
 a =()=>{
console.log(111)
}
b  =() =>{
console.log(222)
}
promise = new Promise((a,b)=>{
    load()
});
function load(){

   var xmlhttp;

   if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera,Safari

      xmlhttp= newXMLHttpRequest();

   }else{// code for IE6, IE5

      xmlhttp= newActiveXObject("Microsoft.XMLHTTP");

   } 

   xmlhttp.onreadystatechange= function(){

      if (xmlhttp.readyState ==4 && xmlhttp.status == 200) {//获得了请求数据

 var expertinfolist = xmlhttp.responseText;

   //发送请求数据到myDiv     document.getElementById("myDiv").innerHTML=expertinfolist;

      }

   }

   var url="expert_ZFTservlet?expert_name="+"曾攀";

   xmlhttp.open("GET", '', true);

   xmlhttp.send();

}
const promise = new Promise((a,b)=>{
    load()
}).then(()=>{
    console.log(111)
}).catch(()=>{
console.log(222)
})

相关文章

网友评论

      本文标题:Promise

      本文链接:https://www.haomeiwen.com/subject/wygqxctx.html