Promise的理解-JavaScript

Posted 恒哥的爸爸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Promise的理解-JavaScript相关的知识,希望对你有一定的参考价值。

1 为什么要使用Promise

防止回调黑洞
      例如,正常的ajax调用,程序员既要定义业务逻辑函数,还要控制这些业务逻辑执行的顺序,一旦异步调用嵌套太深,整个代码可读性和可维护度都会下降。

      例如,实际项目中有如下需求 0.登录 1.查用户配置,2.通过配置权限,查询可以浏览的数据,以上三步肯定是顺序执行的。如下就是直接采用回调方式来实现的代码,而且这里的代码,没有处理错误和异常。估计没几个人愿意看这么多括号的一大组逻辑。

function generalQuery(){
	this.localVal = "hengebb";
  let param = {
      name:'hege',
      key:'001'
  }
  var xhr = new XMLHttpRequest()  
  xhr.open('post', 'http://127.0.0.1:8080/ris/qcTest/lgin.api',JSON.stringify(param))
  xhr.send(null)
  xhr.onreadystatechange = ()=>{
      if (xhr.readyState == 4 && xhr.status == 200) {
      		console.log("@2 local val ="+this.localVal)
          let ret = xhr.responseText
          let retObject = JSON.parse(ret);
          if(retObject.token == '0001'){
              let param = {
                  token:'0001',
              }
              var yhr = new XMLHttpRequest()
              yhr.open('post','http://127.0.0.1:8080/ris/qcTest/config.api', JSON.stringify(param))
              yhr.send(null)
              yhr.onreadystatechange = function(){
              	console.log("@3 local val ="+this.localVal)
                if(yhr.readyState == 4 && yhr.status == 200){
                  let ret = yhr.responseText
                  let retObject = JSON.parse(ret);
                  if(retObject.config == 0){
                      let param =  {
                          token:"0001",
                          config:1
                      }
                      let zhr = new XMLHttpRequest()
                      zhr.open('post','http://127.0.0.1:8080/ris/qcTest/query.api', JSON.stringify(param))
                      zhr.send(null)
                      zhr.onreadystatechange = function(){
                          if(zhr.readyState == 4 &&  zhr.status == 200){
                              let ret = yhr.responseText
                              let retObject = JSON.parse(ret);
                              console.log(retObject.msg)
                          }
                      }
                  }
                }
              }
          }
      }
  }
}

接下来,我们看Promise的处理方式

function login() {
  let param = {
     name:'hege',key:'001'
  }
  return new Promise((resolve)=>{
    // 需要在这里处理异步任务
    var xhr = new XMLHttpRequest();
    xhr.open('post','http://127.0.0.1:8080/ris/qcTest/lgin.api',JSON.stringify(param));
    xhr.send(null);
    xhr.onreadystatechange = function() {
      if(xhr.readyState == 4 && xhr.status == 200) {
        // 获取后台数据
        var ret = xhr.responseText;
        // 成功的情况
        resolve(ret);
      }
    }
  })
}


function config(){
  let param = {
      token:'0001',
  }
  return new Promise((resolve)=>{
    var xhr = new XMLHttpRequest();
    xhr.open('post','http://127.0.0.1:8080/ris/qcTest/config.api',JSON.stringify(param));
    xhr.send(null);
    xhr.onreadystatechange = function() {
      if(xhr.readyState == 4 && xhr.status == 200) {
        // 获取后台数据
        var ret = xhr.responseText;
        // 成功的情况
        resolve(ret);
      }
    }
  })
}

function query(){
	let param =  {
	    token:"0001",
	    config:1
	}
  return new Promise((resolve)=>{
    var xhr = new XMLHttpRequest();
    xhr.open('post','http://127.0.0.1:8080/ris/qcTest/query.api',JSON.stringify(param));
    xhr.send(null);
    xhr.onreadystatechange = function() {
      if(xhr.readyState == 4 && xhr.status == 200) {
        // 获取后台数据
        var ret = xhr.responseText;
        // 成功的情况
        resolve(ret);
      }
    }
  })
}

定义完,每一步的操作后,执行代码如下

function promiseQuery(){
	this.localVal = "hengebb";
	login().then(ret=>{
	  return config();
	}).then(ret=>{
		console.log("@2 ==== this point "+this.localVal)
	  return query();
	}).then(ret=>{
	  console.log(ret.msg)
	})
}

2 Promise的实现原理

2.1 伪代码

function SelfPromise(fn) {
    console.log("1. 注册异步函数")
    this.value = null;
    this.callbacks = [];
    fn.call(this, this.resolve.bind(this))
    //因为调用resolve函数的位置,在客户端,为了把this指针从客户端改变为SelfPromise实例对象,这里使用了bind函数
}
//自定义函数的原型函数
SelfPromise.prototype = {
    constructor: SelfPromise,
    then: function(thenFunc) {
        console.log("3. 注册异步函数回调后,需要执行的函数")
        var obj = {
            onfulfilled: thenFunc
        }
        this.callbacks.push(obj);
        console.log(this.callbacks)
    },
    resolve: function(data) { 
        this.value = data;
        this.callbacks.forEach(item => { //类似执行一个函数链(职责链模式)
            let p = item.onfulfilled(this.value);
            console.log(this)
        });
    }
}
//客户端代码调用
function customPromiseTest(){
  this.promiseTestVal = 99
  let promise = new SelfPromise((resolve)=>{   //1. 将需要异步执行的函数注册到SelfPromise实例中,此函数的格式必须是带有一个形参function(param){}格式(这里是针对SelfPromise的)
      console.log("2. 执行异步函数.... ")       
     //2. 执行注册到SelfPromise实例中的函数. 查看源码,第一步将resolve的this指针指向SelfPromise实例对象;第二部,通过call调用,执行fn异步注册函数
      var param = {							              
          ref_fac_id:1031
      };
      setTimeout(()=>{
          console.log("异步回调开始...")
          let ret = {
              ret:1,name:"hege",token:"10088"
          }
          resolve(ret)
      },1000)
  }).then((ret)=>{    //3. 将异步返回后,需要后续执行的函数也注册到Promise的类中,
    console.log("4. 回调过程调用在步骤3中注册的函数") 
    //4. 回调函数开始执行,在then调用时,注册的函数
    console.log(ret.name + ret.token + " this." + this.promiseTestVal)
    //使用()=>{}方式的函数,这种函数没有自己的构造器,所以,就没有this指针,就只能是用括号外部的this指针
  })
}

在以上的代码中,我做了一些注释。

          实际上,客户调用代码,首先将异步函数1和异步函数回调后,希望后续执行函数2,这两个函数注册到Promise中;Promise内部,含有一个类似包装器功能的函数resolve,用此函数后续调用一个函数链,这些函数链上的函数,就是通过then注册到内部的函数。
          这里需要注意的点,还有如下几个,resolve函数的this指针必须bind到Promise函数中,因为执行的时候,是在客户端代码块内执行的。
          另外,就是被注册函数最好都采用()=>{}方式,不要采用function的方式。 这样,在客户端的回调函数代码块中,就能直接使用客户端函数的this指针。

         本人将以上的代码都上传到资源内,大家感兴趣的可以下载测试!

以上是关于Promise的理解-JavaScript的主要内容,如果未能解决你的问题,请参考以下文章

Promise的理解-JavaScript

Promise的理解-JavaScript

Promise的理解-JavaScript

精心收集的 48 个 JavaScript 代码片段,仅需 30 秒就可理解

JavaScript:理解Promise方法

JavaScript Promise理解