Promise的学习

Promise的学习

(一)Promise介绍与基本使用

1、Promise的理解

  1. 抽象表达

    • Promise是一门新的技术(ES6规范)
    • Promise是JS中进行异步编程的新解决方案
    • (旧方案是单纯使用回调函数)
  2. 具体表达

    • 从语法上来说:Promise是一个构造函数
    • 从功能上来说:Promise对象用来封装一个异步操作并可以获取其成功或者失败的结果值

2、为什么使用Promise

  1. 指定回调函数的方式更加灵活

    • 旧的:必须在启动异步任务前指定
    • Promise:启动异步任务 => 返回Promise对象 => 给 Promise对象绑定回调函数(甚至可以在异步任务结束后指定多个)
  2. 支持链式调用,可以解决回调地狱问题

    • 什么是回调地狱?回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
asyncFunc1(opt,(...args1)=>{
	asyncFunc2(opt,(...args2)=>{
    asyncFunc3(opt,(...args3)=>{
      asyncFunc4(opt,(...args4)=>{
        // some operation
      })
    })
  })
})
  • 回调地狱的缺点? 不便于阅读、不便于异常处理
  • 解决方案?promise链式调用

Promise封装AJAX请求

  1. 原生AJAX请求
const xhr = new XMLHttpRequest;

xhr.open('GET','https://api.apiopen.top/getJoke');

xhr.send();

xhr.onreadystatechange = function(){
	if(xhr.readyState === 4){
		if(xhr.status >=200 && xhr.status < 300){
			console.log(xhr.response);
		}else{
			console.log(xhr.status)
		}
	}
}
  1. 利用Promise请求AJAX
const p = new Promise((resolve,reject)=>{
           //1、创建对象
    const xhr = new XMLHttpRequest;

//2、初始化
    xhr.open('GET','https://api.apiopen.top/getJoke');

    //3、发送
    xhr.send();

    //4、处理响应结果

    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
        //判断响应状态码
        if(xhr.status >=200 && xhr.status <300){
          //控制台输出响应体
          resolve(xhr.response);
        }else{
          reject(xhr.status);
        }
      }
    }
    }).then(res => {
      console.log(res)
    }).catch(err => {
      console.log(err)
    })
 

Promise封装fs模块

function mineReadFile(path){

  return new Promise((resolve,reject)=>{
    require('fs').readFile(path,(err,data)=>{
      if(err) reject();
      resolve();
    })
  })
} 

unti.promisify方法

传入一个遵循常见错误优先的回调风格的函数((err,value) =>...),并返回一个promise版本

最常见的就是fs的readfile函数

const util = require('util');
const fs = require('fs');

let mineReadFile = util.promisify(fs.readFile);

mineReadFile('./Promise/resource/context.txt').then(res => {
  console.log(res.toString(),'haha');
}).catch(err => {
  console.log(err)
})

Promise的三种状态

异步操作之后会有三种状态:

  1. pending:等待状态,比如正在进行网络请求,或者定时器没有到时间
  2. fulfill / resolved:满足状态,当主动回调了resolve时,就处于该状态,并且会回调.then( )
  3. reject:拒绝状态,当主动回调了reject时,就处于该状态,并且会回调.catch( )

Promise的值

实例对象中的另一个属性 「PromiseResult」
保存着异步任务「成功/失败」的结果

  • resolve
  • reject

Promise的基本流程

执行异步操作成功,执行resolve
执行异步操作失败,执行reject
new Promise
Promise对象resolve状态
Promise对象reject状态
回调onResolved
回调onRejected
新的Promise对象

(二) Promise API

(三)Promise 关键问题

  1. 如何改变promise的状态?

    • resolve(value):如果当前是pending就会变为resolved
    • reject(reason): 如果当前是pending就会变为rejected
    • 抛出异常throw:如果当前是pending就会变为rejected
  2. 一个promise指定多个成功/失败回调函数,都会调用吗?

    • 当promise改变为对应状态时都会调用
  3. promise如何串连多个操作任务

    • promise的then返回一个新的promise,可以看成then()链式调用
    • 通过then的链式调用串连多个同步、异步任务
  4. promise异常穿透?

    • 当使用promise的then链式调用时,可以在最后指定失败的回调
    • 前面任何操作出了异常,都会传到最后失败的回调中处理
  5. 中断promise链?

    • 当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
    • 办法:在回调函数中返回一个pending状态的promise对象

(四)Promise 自定义封装

1、定义整体结构

/*
** 手写Promise
*/

function Promise(executor){

}

//添加 then 方法
Promise.prototype.then = function(onResolved,onRejected){}

2、resolve与reject函数的搭建

/*
** 手写Promise
*/

function Promise(executor){

  //resolve 函数
  function resolve(data){

  }
  //reject 函数
  function reject(data){


  }
  //同步调用「执行器函数」
  executor();
}

//添加 then 方法
Promise.prototype.then = function(onResolved,onRejected){}

3、resolve与reject函数的实现

/*
** 手写Promise
*/

function Promise(executor){

  //添加属性
  this.PromiseState = 'pending';
  this.PromiseResult = null;

  const self = this;
  //resolve 函数
  function resolve(data){
    //1、修改对象的状态
    self.PromiseState = 'fulfilled'

    //2、设置对象结果值
    self.PromiseResult = data
  }
  //reject 函数
  function reject(data){
    //1、修改对象的状态
    self.PromiseState = 'rejected'

    //2、设置对象结果值
    self.PromiseResult = data

  }
  //同步调用「执行器函数」
  executor(resolve,reject);
}

//添加 then 方法
Promise.prototype.then = function(onResolved,onRejected){}

4、thorw抛出异常改变状态

使用try{}catch(e)捕捉异常;

  try{
    executor(resolve,reject);
  }catch(e){
    reject(e);
  }

5、Promise对象状态只能修改一次

在resolve,reject函数内部添加if(self.PromiseState !== 'pending') return 语句即可

6、then执行回调

//添加 then 方法
Promise.prototype.then = function(onResolved,onRejected){
  if(this.PromiseState === 'fulfilled'){
    onResolved(this.PromiseResult);
  }
  if(this.PromiseState === 'rejected'){
    onRejected(this.PromiseResult);
  }

7、异步任务回调的执行

首先存储回调函数,等待状态改变后再执行

//添加 then 方法
Promise.prototype.then = function(onResolved,onRejected){
  if(this.PromiseState === 'fulfilled'){
    onResolved(this.PromiseResult);
  }
  if(this.PromiseState === 'rejected'){
    onRejected(this.PromiseResult);
  }
  if(this.PromiseState === 'pending'){
    //保存回调函数
    this.callback = {
      onResolved,
      onRejected
    }
  }
}

function Promise(executor){

  //添加属性
  this.PromiseState = 'pending';
  this.PromiseResult = null;

  this.callback = {};

  const self = this;
  //resolve 函数
  function resolve(data){
    //1、修改对象的状态
    if(self.PromiseState !== 'pending') return 
    self.PromiseState = 'fulfilled'

    //2、设置对象结果值
    self.PromiseResult = data

    //3、执行回调函数
    if(self.callback.onResolved){
      self.callback.onResolved(data);
    }
  }
  //reject 函数
  function reject(data){

    if(self.PromiseState !== 'pending') return 
    //1、修改对象的状态
    self.PromiseState = 'rejected'

    //2、设置对象结果值
    self.PromiseResult = data

    //3、执行回调函数
    if(self.callback.onRejected){
      self.callback.onRejected(data);
    }
  }
  //同步调用「执行器函数」
  try{
    executor(resolve,reject);
  }catch(e){
    reject(e);
  }
  
}

8、指定多个回调的实现

使用数组存储回调函数,通过数组遍历进行调用多个回调函数

  this.callbacks = [];//修改为数组
  if(this.PromiseState === 'pending'){
    //保存回调函数
    this.callbacks.push({
      onResolved,
      onRejected
    })
  }
  //resolve 函数
  function resolve(data){
    //1、修改对象的状态
    if(self.PromiseState !== 'pending') return 
    self.PromiseState = 'fulfilled'

    //2、设置对象结果值
    self.PromiseResult = data

    //3、执行回调函数
    self.callbacks.forEach(item => {
      item.onResolved(data)
    })
  }
  //reject 函数
  function reject(data){

    if(self.PromiseState !== 'pending') return 
    //1、修改对象的状态
    self.PromiseState = 'rejected'

    //2、设置对象结果值
    self.PromiseResult = data

    //3、执行回调函数
    self.callbacks.forEach(item => {
      item.onRejected(data)
    })
  }

9、同步修改状态then方法结果返回

Promise返回是一个Promise对象

所以:

Promise.prototype.then = function(onResolved,onRejected){
  return new Promise((resolve,reject) => {
    if(this.PromiseState === 'fulfilled'){
      try{
        let result = onResolved(this.PromiseResult);

        if(result instanceof Promise){
        //如果result是一个Promise对象,那么返回的Promise对象的状态根据result的状态来定
          result.then(v => {
            resolve(v)
          },r => {
            reject(r)
          })
        }else{
          //对象的状态为「成功」
          resolve(result);
        }
      }catch(e){
        reject(e);
      }
    }
    if(this.PromiseState === 'rejected'){
      onRejected(this.PromiseResult);
    }
    if(this.PromiseState === 'pending'){
      //保存回调函数
      this.callbacks.push({
        onResolved,
        onRejected
      })
    }
  })
}

10、异步修改状态then方法结果返回

	const self = this;
	...
	...
    if(this.PromiseState === 'pending'){
      //保存回调函数
      this.callbacks.push({
        onResolved:function(){
          try{
            let result = onResolved(self.PromiseResult);

            if(result instanceof Promise){
              result.then(v => {resolve(v)}, r => {reject(r)});
            }else{
              resolve(result);
            }
          }catch(e){
            reject(e);
          }
        },
        onRejected:function(){
          try{
            let result = onRejected(self.PromiseResult);

            if(result instanceof Promise){
              result.then(v => {resolve(v)}, r => {reject(r)});
            }else{
              resolve(result);
            }
          }catch(e){
            reject(e);
          }
        }
      })
    }

11、then方法完善与优化

对重复性的代码进行封装

Promise.prototype.then = function(onResolved,onRejected){
  return new Promise((resolve,reject) => {
    const self = this;

    function callback(type){
      try{
        let result = type(self.PromiseResult);

        if(result instanceof Promise){
          result.then(v => {
            resolve(v)
          },r => {
            reject(r)
          })
        }else{
          //对象的状态为「成功」
          resolve(result);
        }
      }catch(e){
        reject(e);
      }
    }

    if(this.PromiseState === 'fulfilled'){
      callback(onResolved);
    }
    if(this.PromiseState === 'rejected'){
      callback(onRejected);
    }
    if(this.PromiseState === 'pending'){
      //保存回调函数
      this.callbacks.push({
        onResolved:function(){
          callback(onResolved)
        },
        onRejected:function(){
          callback(onRejected)
        }
      })
    }
  })
}

12、catch方法-异常穿透与值传递

添加catch方法

//添加 catch方法

Promise.prototype.catch = function(onRejected){
  return this.then(undefined, onRejected);
}

异常穿透与值传递

Promise.prototype.then = function(onResolved,onRejected){
  return new Promise((resolve,reject) => {
    const self = this;
	
	//异常穿透
    if(typeof onRejected !== 'function'){
      onRejected = reason => {
        throw reason
      }
    }
	
	//值传递
    if(typeof onResolved !== 'function'){
      onResolved = value => value
    }

    function callback(type){
      try{
        let result = type(self.PromiseResult);

        if(result instanceof Promise){
          result.then(v => {
            resolve(v)
          },r => {
            reject(r)
          })
        }else{
          //对象的状态为「成功」
          resolve(result);
        }
      }catch(e){
        reject(e);
      }
    }

    if(this.PromiseState === 'fulfilled'){
      callback(onResolved);
    }
    if(this.PromiseState === 'rejected'){
      callback(onRejected);
    }
    if(this.PromiseState === 'pending'){
      //保存回调函数
      this.callbacks.push({
        onResolved:function(){
          callback(onResolved)
        },
        onRejected:function(){
          callback(onRejected)
        }
      })
    }
  })
}

13、Promise.resolve方法

//添加 Promise.resolve方法
Promise.resolve = function(value){
  return new Promise((resolve, reject) => {
    if(value instanceof Promise){
      value.then(v => { 
        resolve(v)
      }, r => {
        reject(r)
      })
    }else{
      resolve(value)
    }
  })
}

14、Promise.reject方法

//添加 Promise.reject方法
Promise.reject = function(reason){
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}

15、Promise.all方法

//添加 Promise.all 方法
Promise.all = function(promises){
  return new Promise((resolve, reject) => {
    let count = 0;
    let arr = [];
    promises.forEach((item, index) => {
      item.then(v => {
        //每个对象都成功,返回的才会是成功的
        count++;
        arr[index] = v;

        if(count === promises.length){
          resolve(arr);
        }
      }, r => {
        reject(r)
      })
    })
  })
}

16、Promise.race方法

//添加 Promise.race方法
Promise.race = function(promises){
  return new Promise((resolve, reject) => {
    promises.forEach((item) => {
      item.then(v => {
        resolve(v);
      }, r => {
        reject(r)
      })
    })
  })
}

17、then回调的异步执行

//Promise.prototype.then

    if(this.PromiseState === 'fulfilled'){
      setTimeout(() => {
        callback(onResolved);
      })
      
    }
    if(this.PromiseState === 'rejected'){
      setTimeout(() => {
        callback(onRejected);
      })   
    }
//function Promise()

...
//异步执行回调函数
 setTimeout(()=>{
      self.callbacks.forEach(item => {
        item.onResolved(data)
      })
    })

async 与 await

async函数

  • 函数的返回值为promise对象
  • promise对象的结果由async函数执行的返回值决定
async function func(){
  return 'promise'
}

console.log(func());

//Promise { 'promise' }

await表达式

  • await右侧的表达式一般为promise对象,但也可以是其他的值
  • 如果表达式是promise对象,await返回的是promise成功的值
  • 如果表达式是其他值,直接将此值作为await的返回值

注意

  • await必须写在async函数中,但async函数中可以没有await
  • 如果await的promise失败了,就会抛出异常,需要通过try…catch捕获异常

(五)async与await结合

读取文件内容

const fs = require('fs');
const util = require('util')

const mineReadFile = util.promisify(fs.readFile)

async function main(){
  let data = await mineReadFile('./Promise/resource/context.txt');
  console.log(data.toString())
}

main();

结合发送AJAX请求

function sendAJAX(url){
  const p = new Promise((resolve,reject)=>{
    //1、创建对象
      const xhr = new XMLHttpRequest;
    
    //2、初始化
      xhr.open('GET',url);
    
    //3、发送
      xhr.send();
    
    //4、处理响应结果
    
      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
          //判断响应状态码
          if(xhr.status >=200 && xhr.status <300){
            //控制台输出响应体
            resolve(xhr.response);
          }else{
            reject(xhr.status);
          }
        }
      }
      }).then(res => {
        console.log(res)
      }).catch(err => {
        console.log(err)
      })
    
}

async function awaitAJAX(){
  let data = await sendAJAX('https://api.apiopen.top/getJoke');
}

你可能感兴趣的:(前端知识,javascript,前端,promise)