• Promise 一自我总结


    最近一下班就没有持续学习,已经持续了几个月,回家就是练练字,看看书,干些七七八八的事。这样的生活总感觉空落落的,毕业半年了,总感觉自己很多东西明白的不清不楚,希望接下来的每一天都能扎扎实实学好。

    一、出现前景,Promise是什么

    • 出现前景:在一个单线程的世界,为了支持异步编程,于是Js使用了回调,然而回调带来的问题就是我们经常听到的什么地狱回调,金字塔等问题。有问题就会有解决方案,所以就出现了Promise。
    • 概念:异步编程的一个解决方案 

    二、基本使用规则

    ES6 规定,Promise对象是一个构造函数,用来生成Promise实例

    const promise = new Promise(function(resolve, reject) {
      if (操作成功){
         resolve(value);
      } else {
         reject(error);
      }
    });

    从上面代码可以看到Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,此外这两个参数是连两个函数,由JS引擎提供。

    那么这两个函数的作用是:

      (1) resolve: 将Promise对象的状态从未完成变成成功,异步操作成功时调用。

      (2) reject: 将Promise对象的状态从未完成变成失败,异步操作失败时调用。

    当Promise实例生成后,就可以用then方法指定resolve状态和reject状态的回调函数,then方法接受两个回调函数作为参数:

    第一个回调是当Promise对象的状态为resolve时调用

    第二个回调函数是当Promise对象的状态为reject时调用,第二个回调函数可选

    基本案例:

    var promise = new Promise(function(resolve, reject) {
      resolve()
      console.log('我是当前脚本的同步任务,then方法指定的回调函数等下我,你的在我后面排队')
    })
    promise.then(function(resolve) {
      console.log('promise的状态为resolve,所以我被调用出来啦')
     },function(reject) {
      console.log('promise的状态为reject,我才会出来哦,这次我就不参与')
    })
    
    
    结果输出:
    我是当前脚本的同步任务,then方法指定的回调函数等下我,你的在我后面排队
    promise的状态为resolve,所以我被调用出来啦

    Promise.prototype.then()

    Promise 实例具有then方法, 那么then方法是定义在原型对象promise.prototype上的
    then方法返回的也是一个新的Promise实例,因此可以采用链式写法,即then方法后面再调用另一个then方法

    run1.then(function(data) {
       console.log(data + ':第一个输出')
       return run2();
     }).then(function(data) {
       console.log(data + ':第二个输出')
       return run3()
    }).then(function(data) {
       console.log(data + ':第三个输出')
    })

    上面的代码使用then方法,第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

    Promise.prototype.catch():

    用于指定发生错误时的回调函数

    run1.then(function(data) {
       return run2();
    }).catch(function(data) {
       console.log('前方有异常')
    })

    看到这里的时候,可能会觉得promise对象不是有一种失败调用的reject函数么,如下格式:

    run1.then(function(data) {
       return run2();
    }, function(data) {
       console.log('异常调用')
    })

    对比这两种写法,第一种写法要好于第二种写法,理由是通过catch可以捕获前面then方法执行中的错误,更接近同步的写法(try/catch)。

    因此,建议总是使用catch方法,而不使用then方法的第二个参数。

    下面是一个用Promise对象实现的 Ajax 操作的例子:

      function getData(url) {
        let promise = new Promise(function(resolve, reject) {
          const handler = function() {
            if (this.readyState !== 4) {
              return
            }
            if (this.status === 200) {
              resolve(this.response)
            } else {
              reject(new Error(this.responseText))
            }
          }
          let temp = new XMLHttpRequest()
          temp.open('get', url)
          temp.onreadystatechange = handler;
          temp.setRequestHeader("Accept", "application/json");
          temp.send()
        })
        return promise
      }
      
      getData('http://yapi.demo.qunar.com/mock/37644/testDat').then(function(data){
        console.log(data)
        return '12'
      }).then(function(data) {
        console.log(data)
      })

    三、手写一个promise

    * 同步操作,且then只能调用一次

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title>同步操作,且then只能调用一次</title>
    </head>
    <body>
    
    <div id="box">12</div>
    <script>function Promise(exector) {  // 创建一个构造构造函数
          let self = this;
          this.value = undefined;
          this.reason = undefined;
          this.status = 'pending';
          function resolve(value) { // 此function this指向window
            if (self.status === 'pending') {
              self.value = value;
              self.status = 'resolved';
            }
          }
          function reject(reason) {
            if (self.status === 'pending') {
              self.reason = reason;
              self.status = 'rejected';
            }
          }
          try {
            exector(resolve, reject)
          } catch(e) {
            reject(e)
          }
        }
        Promise.prototype.then = function(onFulfilled, onRejected) {  // then方法添加到构造函数的原型上
          let self = this;
          if (this.status === 'resolved') {
            onFulfilled(self.value)
          }
          if (this.status === 'rejected') {
            onRejected(self.reason)
          }
        }
    
        let promise1 = new Promise(function(a, b) {
          a('ha')
        })
        promise1.then(data => {
          console.log('success')
          console.log(data); 
        }, err=> {
          console.log('error')
          console.log(err);
        })
    </script>
    </body>
    </html>

    显然我们平时用的都是异步的方式,再来改改。需要在构造函数中存放两个数组,分别保存成功回调和失败的回调 因为可以then多次,所以需要将这些函数放在数组中

    * 异步操作,且then只能调用一次

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title>异步操作,且then只能调用一次</title>
    </head>
    <body>
    
    <div id="box">12</div>
    <script>
        function Promise(exector) {  // 创建一个构造构造函数
          let self = this;
          this.value = undefined;
          this.reason = undefined;
          this.status = 'pending';
          this.successCallBack = [];
          this.failCallBack = [];
          function resolve(value) { // 此function this指向window
            if (self.status === 'pending') {
              self.value = value;
              self.status = 'resolved';
              console.log(self.successCallBack)
              self.successCallBack.forEach((fn) => { fn() }) // 遍历then中成功的所有回调函数
            }
          }
          function reject(reason) {
            if (self.status === 'pending') {
              self.reason = reason;
              self.status = 'rejected';
              self.failCallBack.forEach((fn) => { fn() }) // 遍历then中失败的所有回调函数
            }
          }
          try {
            exector(resolve, reject)
          } catch(e) {
            reject(e)
          }
        }
        Promise.prototype.then = function(onFulfilled, onRejected) {  // then方法添加到构造函数的原型上
          let self = this;
          if (this.status === 'resolved') {
            onFulfilled(self.value)
          }
          if (this.status === 'rejected') {
            onRejected(self.reason)
          }
          if (this.status === 'pending') {
            this.successCallBack.push(() => {
              onFulfilled(self.value)
            })
            this.failCallBack.push(() => {
              onRejected(self.reason)
            })
          }
        }
    
        let promise1 = new Promise((resolve, reject) => {
          setTimeout(() => {
            if(Math.random() > 0.5) {
              resolve('成功');
            } else {
              reject('失败');
            }
          }, 1000)
        })
    
        promise1.then(data => {
          console.log('success')
          console.log(data); 
        }, err=> {
          console.log('error')
          console.log(err);
        })
    </script>
    </body>
    </html>

    * 异步操作,then可以链式调用

  • 相关阅读:
    jQuery学习笔记3--网页字体变大变小
    jQuery学习笔记2--表格内容筛选
    jQuery学习笔记1--表格展开关系
    (转)PhoneGap开发环境搭建
    对HTML+CSS+JavaScript的个人理解
    (转)经典收藏 50个jQuery Mobile开发技巧集萃
    (转)phoneGap-Android开发环境搭建
    (转)面向移动设备的HTML5开发框架
    (转)前端攻略系列(二)
    (转)常见浏览器兼容性问题与解决技巧
  • 原文地址:https://www.cnblogs.com/Tiboo/p/10072963.html
Copyright © 2020-2023  润新知