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可以链式调用

原文地址:https://www.cnblogs.com/Tiboo/p/10072963.html