Promise的理解和使用

Promise的理解和使用

1.什么是Promise?

1.1.Promise理解

1.Promise是当前前端最好的异步处理方案,(旧方案是单纯的使用回调函数)
异步编程:
fs 文件操作,

require('fs').readFile('./index.html',(err,data)=>{})

数据库操作,
ajax,

$.get('/sever',(data)=>{})

定时器,

setTimeout(()=>{},2000)

2.从语法上来说: Promise是一个构造函数,
从功能上来说:Promise是一个对象,用来封装一个异步操作,获取其成功/失败的结果

1.2.Promise的状态(PromiseState)

实例对象Promise的一个属性 PromiseState
Promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);
1.这三种状态的变化途径只有2种:
①异步操作从 未完成 pending => 已完成 resolved
②异步操作从 未完成 pending => 失败 rejected
状态一旦改变,就无法再次改变状态,这也是它名字 Promise-承诺 的由来,一个Promise对象只能改变一次,状态一旦改变,就不会再变。
成功的结果数据一般称为value,失败的结果数据一般称为reason

1.3.Promise对象的值(PromiseResult)

实例对象Promise的另一个属性 PromiseResult
保存着对象 成功/失败 的结果(value/reason)
resolve/reject这两个函数可以对实例对象中的这个值进行修改

1.4.Promise的工作流程

在这里插入图片描述

2.为什么要用Promise?

2.1.支持链式调用,可以解决回调地狱问题

什么是回调地狱?
回调地狱是在异步代码中使用函数回调变得模糊或难以遵循的任何代码,简单来说就是异步回调函数的嵌套.不便于阅读,不便于异常处理

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result:' + finalResult)
    }, failureCallback)
  }, failureCallback)
}, failureCallback)

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

旧的:必须在启动异步任务前指定

// 1. 纯回调的形式
// 成功的回调函数
function successCallback(result) {
  console.log("声音文件创建成功:" + result);
}
// 失败的回调函数
function failureCallback(error) {
  console.log("声音文件创建失败:" + error);
}
// 必须先指定回调函数,再执行异步任务
createAudioFileAsync(audioSettings, successCallback, failureCallback) // 回调函数在执行异步任务(函数)前就要指定

promise:启动异步任务=>返回promie对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定/或者绑定多个)

// 2. 使用Promise
const promise = createAudioFileAsync(audioSettings);  // 执行2秒
setTimeout(() => {
  promise.then(successCallback, failureCallback) // 也可以获取
}, 3000);

3.Promise的API

3.1.Promise构造函数:Promise(executor){}

(1) excutor 函数: 执行器 (resolve,reject)=>{}
(2) resolve 函数: 内部定义成功时我们调用的函数 value=>{}
(3) reject 函数:内部定义失败时我们调用的函数 reason=>{}
说明: executor会在 Promise内部立即同步调用,异步操作在执行器中执行

let p = new Promise((resolve, reject) => {
  //同步调用,会先输出111,再输出222
  console.log(111)
})
console.log(222)
3.2.Promise.prototype.then 方法: (onResolved,onRejected)=>{}

(1) onResolved 函数: 成功的回调函数 (value)=>{}
(2) onRejected函数: 失败的回调函数 (reason)=>{}
说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 Promise 对象

3.3.Promise.prototype.cath方法: (onRejected)=>{}

(1) onRejected 函数: 失败的回调函数 (reason)=>{}

let p = new Promise((resolve, reject) => {
  reject('error')
})
p.catch(reason => console.log(reason))
3.4.Promise.resolve方法: (value)=>{}

(1) value:成功的数据或promise对象
说明:返回一个成功/失败的promise对象

//如果传入的参数为非 Promise类型的对象,则返回的结果为成功的Promise对象
//如果传入的参数为 Promise类型的对象,则参数的结果决定了 resolve 的结果
let p1 = Promise.resolve(111)
console.log(p1)//Promise { 111 }
let p2 = Promise.resolve(new Promise((resolve, reject) => {
  reject('erro')
}))
p2.catch(reason => console.log(reason))//erro
3.5. Promise.reject方法:(reason)=>{}

(1) reason:失败的原因
说明:返回一个失败的promise对象

//不管传入什么都是失败
let p1 = Promise.reject(111)
console.log(p1)//
let p2 = Promise.reject(new Promise((resolve, reject) => {
  resolve('ok')
}))
console.log(p2);
3.6. Promise.all方法:(promises)=>{}

(1) promises:包含n 个promise的数组
说明:返回一个新的promise,只有所有的 promise都成功才成功,只要有一个失败了就直接失败

//只有当所有的promise都成功才成功
let p1 = new Promise((resolve, reject) => {
  resolve('ok')
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('Yeah')
const result = Promise.all([p1, p2, p3])
console.log(result)//结果是所有promise成功的结果组成的一个数组
3.7.Promise.race方法: (promises)=>{}

(1) promises: 包含n个promise 的数组
说明:返回一个新的 promise,第一个完成的promise的结果状态就是最终的结果状态

 let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 1000)
    })
    let p2 = Promise.resolve('Success')
    let p3 = Promise.resolve('Yeah')
    const result = Promise.race([p1, p2, p3])
    console.log(result) 

4.Promise的基本使用

这里有一个随机抽奖案例,让我们来一下promise是怎么使用的
点击按钮,2s后显示是否中将(30%中奖概率)
若中奖,弹出恭喜您,奖品为小米12Ultra一台.
未中奖则弹出再接再厉

<div>
    <h2>Promise初体验</h2>
    <button class="btn">点击抽奖</button>
  </div>
  <script>
    // 得到一个两数之间的随机整数,包括两个数在内
    function getRandom (min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    //获取对象
    const btn = document.querySelector('.btn')
    //绑定单击事件
    btn.addEventListener('click', function () {
      //resolve 解决 函数类型的数据
      //reject 拒绝 函数类型的数据
      const p = new Promise((resolve, reject) => {
        setTimeout(() => {
          let n = getRandom(1, 100)//获取1-100的一个随机数
          //resolve()可以将 promise 对象的状态设置为[成功]
          //reject()可以将 promise 对象的状态设置为[失败]
          n < 30 ? resolve(n) : reject(n)
        }, 2000)
      })
      //调用then方法
      p.then((value) => {
        alert(`恭喜您,奖品为小米12Ultra一台,中奖号码为${value}`)
      }, (reason) => {
        alert(`再接再厉,号码为${reason}`)
      })
    })
  </script>
4.1.promise fs模块
promise 读取文件:
const fs = require('fs')
//回调函数形式
// fs.readFile('./content/content.text', (err, data) => {
//   //如果出现错误,抛出错误
//   if (err) throw err
//   //输出文件内容
//   console.log(data.toString());
// })
//promise形式
let p = new Promise((resolve, reject) => {
  fs.readFile('./content/content.text', 'utf8', (err, data) => {
    if (err) reject(err)
    resolve(data)
  })
})
p.then(value => console.log(value), reason => console.log(reason))
promise 封装读取文件:
//封装一个函数 myReadFile 读取文件
// 参数: path 文件路径
// 返回: promise 对象
function myReadFile (path) {
  return new Promise((resolve, reject) => {
  //读取文件
    require('fs').readFile(path, 'utf8', (err, data) => {
    //判断
      if (err) reject(err)
      resolve(data)
    })
  })
}
myReadFile('./content/content.text')
  .then(value => console.log(value), reason => { console.log(reason); })
util.promisify() 方法:

Node.js 内置的 util 模块有一个 promisify() 方法,该方法将基于回调的函数转换为基于 Promise 的函数。

// util.promisify 方法
//引入 util 模块
const util = require('util')
//引入 fs 模块
const fs = require('fs')
//返回一个新的函数
const myReadFile = util.promisify(fs.readFile)
myReadFile('./content/content1.text', 'utf8')
  .then(value => console.log(value), reason => console.log(reason))

4.2.promise AJAX请求
promise 与 AJAX 的简单使用:
<div class="container">
    <div>Promise 封装 AJAX 操作</div>
    <button class="btn">点击发送</button>
  </div>
  <script>
    const btn = document.querySelector('.btn')
    btn.addEventListener('click', function () {
      //创建Promise
      const p = new Promise((resolve, reject) => {
        //创建对象
        const xhr = new XMLHttpRequest()
        xhr.responseType = 'json'
        //初始化
        xhr.open('GET', 'https://netease-cloud-music-api-chi-sage.vercel.app/personalized?limit=10')
        //发送
        xhr.send()
        //处理响应结果
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            //判断响应状态码2xx
            if (xhr.status >= 200 && xhr.status < 300) {
              //控制台输出响应体
              resolve(xhr.response)
            } else {
              //控制台输出响应状态码
              reject(xhr.status)
            }
          }
        }
      })
      p.then(value => console.log(value), reason => console.warn(reason))
    })
  </script>

promise 封装 AJAX 请求:
  function sendAJAX (url) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.responseType = 'json'
        xhr.open('GET', url)
        xhr.send()
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              resolve(xhr.response)
            } else {
              reject(xhr.status)
            }
          }
        }
      })
    }
    sendAJAX('https://netease-cloud-music-api-chi-sage.vercel.app/pesonalized?limit=10')
      .then(value => console.log(value), reason => console.log(reason))

5.Promise的几个关键问题

5.1如何改变promise的状态?

(1) resolve(value): 如果当前是 pending 就会变为 resolved
(2) reject(reason): 如果当前是 pending 就会变为 rejected
(3) 抛出异常: 如果当前是 pending 就会变为 rejected

 let p = new Promise((resolve, reject) => {
      // 1. resolve 函数
      // resolve('ok') //pending => fulfilled (resolved)
      // 2. reject 函数 
      // reject('error')//pending => rejected
      // 3. 抛出错误
      // throw '出问题了'
    })
    console.log(p)
5.2一个Promise指定多个成功/失败回调函数,都会调用吗?

当Promise改变为对应状态时都会调用

  const p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    //指定回调1
    p.then(value => console.log(value))//ok
    //指定回调2
    p.then(value => console.log(value))//ok

5.3改变Promise状态和指定回调函数谁先谁后?

1.都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调
2.如何先改变状态再指定回调?
如果执行器中是同步任务,直接调用resolve()/reject()则先改变状态再指定回调.
如果执行器中是异步任务任务,需要等待一段时间再调用resolve()/reject(),延长更长的时间调用then()则也是先改变状态再指定回调.
注意:then方法只是指定回调,不会立即执行回调函数,resolve()/reject()后才会去执行回调函数

 let p = new Promise((resolve, reject) => {
      //如果执行器中是同步任务,直接调用resolve()/reject()则先改变状态再指定回调.
      // resolve('ok')
      //如果执行器中是异步任务,需要等待一段时间再调用resolve()/reject(),则先指定回调,后改变状态
      setTimeout(() => {
        resolve('ok')
      }, 1000)
    })
    p.then(value => console.log(value))

3.什么时候才能得到数据?(回调函数什么时候执行)
不论什么情况都是先改变状态,再执行回调函数

5.4Promise.then()返回的新promise的结果状态由什么决定?

(1)简单表达: 由then()指定的回调函数执行的结果决定
(2)详细表达:
①如果抛出异常,新promise变为 rejected, reason为抛出的异常
②如果返回的是非promise的任意值,新 promise变为resolved, value为返回的值
③如果返回的是另一个新promise,此promise的结果就会成为新promise 的结果

  let p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    //执行then方法
    let result = p.then(value => {
      // 1. 抛出错误
      // throw '出了问题' //result 为失败
      // 2.返回非promise类型的对象
      // return '123' //result 为成功,结果就是返回的值
      // 3.返回promise类型的对象
      return new Promise((resolve, reject) => {
        // resolve('success')//result 为成功,结果就是返回的这个promise对象成功的结果
         reject('error')//result 为失败,结果就是返回的这个promise对象失败的结果
      })
    }, reason => {
      console.log(reason)
    })
    console.log(result)
5.5Promise如何串联多个操作任务?

(1)一个Promise实例对象的回调函数的返回结果依旧是一个Promise对象,那么就可以给这个回调继续指定一个回调,从而串联多个操作任务,形成链式调用。
(2)通过then 的链式调用串连多个同步/异步任务

 let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 1000)
    })
    p.then(value => {
      return new Promise((resolve, reject) => {
        resolve('success')
      })
    }).then(value => {
      console.log(value)//success
    }).then(value => {
      //Promise.then()返回的新promise的结果状态由then()指定的回调函数执行的结果决定
      //前一个.then的确返回了promise对象, 但这个对象的值是由回调函数的返回值决定的, 由于没有声明返回值, 所以返回undefind, 所以这里得到的值就是undefind
      console.log(value)//undefind
    })
5.6Promise异常穿透

1)当使用promise 的 then链式调用时,可以在最后指定失败的回调
(2)前面任何操作出了异常,都会传到最后失败的回调中处理
需要注意的是,异常穿透的前提是前面所有的then中都没有指定失败的回调,这样才能保证在最后的catch中执行失败的回调!

 const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
        // reject('err')
      }, 1000)
    })
    p.then(value => {
      // console.log(111)
      throw 'error'
    }).then(value => {
      console.log(222)
    }).then(value => {
      console.log(333)
    }).catch(reason => {
      console.log(reason)
    })
5.7中断Promise链

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

const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 1000)
    })
    p.then(value => {
      console.log(111)
      //有且只有一个方式,返回一个pending状态的promise
      return new Promise(() => { })
    }).then(value => {
      console.log(222)
    }).then(value => {
      console.log(333)
    }).catch(reason => {
      console.log(reason)
    })

6.自定义(手写)Promise

class Promise {
  //构造方法
  constructor(executor) {
    //添加属性
    this.PromiseState = 'pending'
    this.PromiseResult = null
    //声明一个属性,用来保存回调函数
    this.callbacks = []
    //resolve 函数
    const resolve = data => {
      //判断状态是否已经被修改,被修改了就不能再更改了
      if (this.PromiseState !== 'pending') return
      //修改对象的状态(PromiseState)
      this.PromiseState = 'fulfiled'
      //设置对象的结果值(PromiseResult)
      this.PromiseResult = data
      //异步任务调用成功的回调函数
      setTimeout(() => { this.callbacks.forEach(item => item.onResolved(data)) })
    }
    //reject 函数
    const reject = data => {
      //判断状态是否已经被修改,被修改了就不能再更改了
      if (this.PromiseState !== 'pending') return
      //修改对象的状态(PromiseState)
      this.PromiseState = 'rejected'
      //设置对象的结果值(PromiseResult)
      this.PromiseResult = data
      //异步任务调用失败的回调函数
      setTimeout(() => { this.callbacks.forEach(item => item.onRejected(data)) })
    }
    //throw抛出异常,用try catch捕获异常,并修改promise对象状态为失败
    try {
      //同步调用执行器函数(executor)
      executor(resolve, reject)
    } catch (error) {
      //修改promise对象状态为失败
      reject(error)
    }
  }
  //then 方法
  then (onResolved, onRejected) {
    //异常穿透
    if (typeof onRejected !== 'function') onRejected = reason => { throw reason }
    //值传递
    if (typeof onResolved !== 'function') onResolved = value => value
    return new Promise((resolve, reject) => {
      //封装函数
      const callback = type => {
        try {
          //获取回调函数的执行结果
          let result = type(this.PromiseResult)
          if (result instanceof Promise) {
            //是promise对象
            result.then(value => resolve(value), reason => reject(reason))
          } else {
            //如果不是promise对象结果对象状态为成功
            resolve(result)
          }
        } catch (error) {
          reject(error)
        }
      }
      //调用回调函数
      if (this.PromiseState === 'fulfiled') setTimeout(() => { callback(onResolved) })
      if (this.PromiseState === 'rejected') setTimeout(() => { callback(onRejected) })
      //判断pending状态
      if (this.PromiseState === 'pending') {
        //保存回调函数
        this.callbacks.push({
          onResolved: () => callback(onResolved),
          onRejected: () => callback(onRejected)
        })
      }
    })
  }
  //cath 方法
  cath (onRejected) {
    return this.then(undefined, onRejected)
  }
  //resolve 方法
  static resolve (value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(v => resolve(v), r => reject(r))
      } else {
        resolve(value)
      }
    })
  }
  //reject 方法
  static reject (reason) {
    return new Promise((resolve, reject) => reject(reason))
  }
  //all 方法
  static all (promises) {
    return new Promise((resolve, reject) => {
      //声明变量
      let count = 0
      let arr = []
      promises.forEach((item, index) => {
        item.then(value => {
          count++
          arr[index] = value
          if (count === promises.length) resolve(arr)
        }, reason => reject(reason))
      })
    })
  }
  //race 方法
  static race (promises) {
    return new Promise((resolve, reject) => {
      promises.forEach(item => item.then(value => resolve(value), reason => reject(reason)))
    })
  }
}

7. async 和 await

7.1 async 函数

1.函数的返回值为Promise对象
2.promise对象的结果由async修饰的函数的返回值决定
①如果抛出异常,结果就是失败的promise对象 , 抛出的异常就是失败的结果
②如果返回值非promise的任意值,结果就是成功的promise对象 return后面的值就是成功的结果值
③如果返回值是一个promise对象,结果由此promise的结果决定

 async function main () {
      //1.如果返回值是一个非Promise类型的数据
      // return 521
      //2.如果返回的是一个promise对象
      // return new Promise((resolve, reject) => {
      //   reject('Error')
      // })
      // 3.抛出异常
      throw 'no'
    }
    let result = main()
    console.log(result);
7.2 await 表达式

1.await右侧的表达式一般为promise对象,但也可以是其它的值
2.如果表达式是promise对象, await返回的是promise成功的值
3.如果表达式是其它值,直接将此值作为await的返回值
注意:
1.await 必须写在 async函数中,但 async函数中可以没有await
2.如果await的 promise 失败了,就会抛出异常,需要通过 try…catch捕获处理

 async function main () {
      let p = new Promise((resolve, reject) => {
        // resolve('ok')
        reject('error')
      })
      //1.右侧为promise的情况
      // let res = await p
      // console.log(res);//ok
      //2.右侧为其他类型的值得情况
      // let res = await 111
      // console.log(res);//111
      //3.右侧promise是失败的情况
      try {
        let res = await p
        console.log(res);
      } catch (error) {
        console.log(error);
      }
    }
    main()
7.3 async 和 await 结合应用(then-fs读取文件)
import thenFs from "then-fs";
async function fn () {
  try {
    const a = await thenFs.readFile('./txt/a.txt', 'utf8')
    console.log(a);
    const b = await thenFs.readFile('./txt/b.txt', 'utf8')
    console.log(b);
    const c = await thenFs.readFile('./txt/c.txt', 'utf8')
    console.log(c);
  } catch (error) {
    console.log(error);
  }
}
fn()
7.4 async 和 await 发送axios请求
import axios from 'axios'
async function getBookslist () {
  const { data: res } = await axios.get(url, { params: { id: 1 } })
  console.log(res);
}
getBookslist()
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值