• Promise基础知识


    Promise

    1.Promise的前置小知识#

    • 进程(厂房)

      • 程序的运行环境
    • 线程(工人)

    • 线程是实际进行运算的东西

    • 同步

      • 通常情况代码都是自上向下一行一行执行的
      • 前边的代码不执行后边的代码也不会执行
      • 同步的代码执行会出现阻塞的情况
      • 一行代码执行慢会影响到整个程序的执行
    • 解决同步的问题:

      • java python

        • 通过多线程来解决,但是一般消耗资源比较多
      • node.js

        • 通过异步方式来解决

          我们可以这么理解:客人就好比我们请求的数据,服务员就好比客户端,厨师就好比服务器,我们现在客人点菜,服务员接收到菜的名称信息,给厨师说,厨师开始做,厨师在做的时候,客人一直等,不能干其他的事情,这就是同步,只能干一件事,我们现在利用异步的方式,可以让客人在课桌上等着菜来,也不影响服务员接收下一个客人的点菜,这样就可以很好的处理同步所带来的堵塞问题

    • 异步

      • 一段代码的执行不会影响到其他的程序
      • 异步的问题:
    • 异步的代码无法通过return来设置返回值

    • 特点:

      1. 不会阻塞其他代码的执行

      2. 需要通过回调函数来返回结果

        function sum(a, b, cb) {
            setTimeout(() => {
                cb(a + b)  //调用箭头函数,把结果作为回调函数的参数
            }, 1000)
        }
        
        sum(123, 456, (result)=>{
            console.log(result)
        })
        
    • 基于回调函数的异步带来的问题

      1. 代码的可读性差

      2. 可调试性差(造成回调地狱)

        sum(123, 456, (result)=>{
            sum(result, 7, (result)=>{
                sum(result, 8, result => {
                    sum(result, 9, result => {
                        sum(result, 10, result => {
                            console.log(result)
                        })
                    })
                })
            })
        })
        
    • 解决问题:

      • 需要一个东西,可以代替回调函数来给我们返回结果
      • Promise横空出世
        • Promise是一个可以用来存储数据的对象
          • Promise存储数据的方式比较特殊,这种特殊的方式使得Promise可以用来存储异步调用的数据

    2.Promise介绍#

    异步调用必须要通过回调函数来返回数据,当我们进行一些复杂的调用时,会出现回调地狱

    问题:

    ​ 异步必须通过回调函数来返回结果,回调函数增加就不容易处理

    • Promise
      • Promise可以帮助我们解决异步中的回调函数的问题
      • Promise就是一个用来存储数据的容器
        • 它拥有着一套特殊的存储数据的方式
        • 这个方式使得它里面可以存储异步调用的结果
    1. 创建Promise

      1. 创建Promise时,构造函数中需要一个函数作为参数

      2. Promise构造函数的回调函数,它会在创建Promise时调用,调用时会有两个参数传递进去

        const promise = new Promise((resolve, reject)=>{
            // resolve 和 reject 是两个函数,通过这两个函数可以向Promise中存储数据
            // resolve 在执行正常的时候存储数据, reject 是在执行错误的时候存储数据
            resolve('我是正常执行的时候调用的')
            reject('我是错误执行的时候调用的')
            //通过函数来访问Promise中添加数据,好处就是可以用来添加异步调用的数据
            setTimeout(()=>{
                resolve('异步中调用数据')
        	},2000)
            throw new Error('出错了,调用的是reject')
        })
        
    2. 从Promise中读取数据

      1. 可以通过Promise的实例方法then来读取Promise中存储的数据

      2. then需要两个回调作为参数,回调函数来获取Promise中的数据

        1. 通过resolve存储的数据,会调用第一函数返回,可以在第一个函数中编写处理数据的代码

        2. 通过reject存储数据或者出现异常时,会调用第二个函数返回,可以在第二个函数中编写处理异常的代码

          promise.then((result)=>{
              console.log('1',result)
          },(reson)=>{
              console.log('2',reason)
          })
          
    3. Promise中维护了两个隐藏属性:

      1. PromiseResult
        1. 用来存储数据
      2. PromiseState
        1. 记录Promise的状态(三种状态)
          1. pending(进行中)
          2. fulfilled(完成)通过resolve存储数据时
          3. rejected(拒绝,出错了)出错了或通过reject存储数据时
        2. state只能修改一次,修改以后永远不会在变
      3. 流程:
        1. 当Promise创建时,PromiseState初始值为pending
          1. 当通过resolve存储数据时 PromiseState 变为fulfilled(完成)
            1. PromiseResult变为存储的数据
          2. 当通过reject存储数据或出错时 Promise 变为rejected(拒绝)
            1. PromiseResult变为存储的数据 或 异常对象
        2. 当我们通过then读取数据时,相当于为Promise设置了回调函数
          1. 如果PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据
          2. 如果PromiseState变为rejected。则调用then的第二个回调函数来返回数据
      const promise2 = new Promise((resolve, reject) => {
          resolve("哈哈")
      })
      
      // console.log(promise2)
      promise2.then(result => {
          console.log(result)
      }, reason => {
          console.log("出错了")
      })
      
    4. catch()用法和then类似,但是只需要一个回调函数作为参数

      1. catch() 中的回调只会在Promise被拒绝时才会调用

      2. catch() 相当于 then(null, reason=>{})

      3. catch() 就是一个专门处理Promise异常的方法

        promise2.catch(reason => {
            console.log(222222)
        })
        
    5. finally()

      1. 无论是正常存储数据还是出现异常了,finally总会执行

      2. 但是finally的回调函数中不会接收到数据

      3. finally()通常用来编写一些无论成功与否都要执行的代码

        promise2.finally(()=>{
            console.log("没有什么能够阻挡我执行的!")
        })
        

    3.Promise详解#

    3.1Promise用法#

    Promise就是一个用来存储数据对象,但是由于Promise存取的方式的特殊,所以可以直接将异步调用的结果存储到Promise中

    function sum(a, b) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(a + b)
            }, 1000)
        })
    }
    //回调地狱式写法:
    sum(123, 456).then(result => {
        sum(result, 7).then(result =>{
            sum(result, 8).then(result => {
                console.log(result)
            })
        })
    })
    //promise写法:
    sum(123, 456)
        .then(result => result + 7)
        .then(result => result + 8)
        .then(result => console.log(result))
    

    下来我们就解释一下为什么是这种链式调用的形式:

    ​ promise中的then方法会返回一个新的Promise,而我们接收并且输出的是Promise的PromiseResult的值,就像这样:then (return new Promise()),

    Promise中会存储回调函数的返回值,当前的参数是上一个链式调用的返回值

    promise
        .then(result => {
            console.log("回调函数", result) 
            return "会作为下个then的参数"
        })
        .then(result => {
            console.log("第二个then", result)  //第二个then,会作为下个then的参数
            return "taotao真快乐"
        })
        .then(result => {
            console.log(result)  //taotao真快乐
        })
    

    promise中的

    1. then (return new Promise())
    2. catch
    3. 这三个方法都会返回一个新的Promise, Promise中会存储回调函数的返回值
    4. finally
      1. finally的返回值,不会存储到新的Promise中
    5. 对Promise进行链式调用时,后面的方法(then和catch)读取的上一步的执行结果
      1. 如果上一步执行结果不是当前想要的结果,则跳过当前的方法,执行下一个方法
      2. 一般都把catch写到最后,只写一个,最后统一处理异常
      3. 当Promise出现异常时,而整个调用链中没有catch,则异常会向外抛出

    3.2Promise静态方法#

    1. Promise.resolve() 创建一个立即完成的Promise

      1. Promise.resolve('成功调用时的数据').then(
            (result)=>{
                console.log(1111)
            })
        //相当于
        new Promise((resolve, reject)=>{
        	resolve('成功调用时的数据')
        }).then(result=>{
        	  console.log(1111)
        })
        
    2. Promise.reject() 创建一个立即拒绝的Promise

      1. Promise.reject('错误')
        
    3. Promise.all([...]) 同时返回多个Promise的执行结果

      1. 其中有一个报错,就返回错误

      2. function sum(a, b){
            return new Promise((resolve, reject)=>{
               setTimeout(()=>{
                    resolve(a + b)
               },1000)
        	})
        }
        //传递一个可迭代对象(类数组)
        Promise.all(
        	sum(111, 222),
            Promise.reject("哈哈"),
            sum(222, 333),
        	sum(333, 444)
        ).then((result)=>{
            console.log(result)  //[ 579, 11, 77 ]
        }).catch ((reason)=>{
            console.log(reason)  //'哈哈'
        })
        
    4. Promise.allSettled([...]) 同时返回多个Promise的执行结果(无论成功或失败)

      1. {status: 'fulfilled', value: 579}

      2. {status: 'rejected', reason: '哈哈'}

      3. Promise.allSettled([
            sum(123, 456),
            sum(5, 6),
            Promise.reject("哈哈"),
            sum(33, 44)
        ]).then(r => {
            console.log(r)
        })
        
        //返回的结果如下:
        [
          { status: 'fulfilled', value: 579 },
          { status: 'fulfilled', value: 11 },
          { status: 'rejected', reason: '哈哈' },
          { status: 'fulfilled', value: 77 }
        ]
        
        
    5. Promise.race([...]) 返回执行最快的Promise(不考虑对错)

      1. Promise.race([
            Promise.reject(1111),
            sum(123, 456),
            sum(5, 6),
            sum(33, 44)
        ]).then(r => {
            console.log(r)
        }).catch(r=>{
            console.log(r)
        })
        //执行结果如下
        1111
        Promise.reject(1111),不用等定时器执行结束,直接就调用
        
    6. Promise.any([...]) 返回执行最快的完成的Promise

      1. Promise.any([
            Promise.reject(1111),
            Promise.reject(2222),
            Promise.reject(3333),
        ]).then(r => {
            console.log(r)
        }).catch(r => {
            console.log("错误", r)
        })
        

    4.宏任务和微任务#

    JS是单线程的,它的运行时基于事件循环机制(event loop)

    • 调用栈
        • 栈是一种数据结构,后进先出
      • 调用栈中,放的是要执行的代码
    • 任务队列
      • 队列
        • 队列是一种数据结构,先进先出
      • 任务队列的是将要执行的代码
      • 当调用栈中的代码执行完毕后,队列中的代码才会按照顺序依次进入到栈中执行
      • 在JS中任务队列有两种
      • 宏任务队列 (大部分代码都去宏任务队列中去排队)
      • 微任务队列 (Promise的回调函数(then、catch、finally))
    • 整个流程
    • ① 执行调用栈中的代码
    • ② 执行微任务队列中的所有任务
    • ③ 执行宏任务队列中的所有任务

    我们开始小试一下

        Promise的执行原理
            - Promise在执行,then就相当于给Promise了回调函数
                当Promise的状态从pending 变为 fulfilled时,
                    then的回调函数会被放入到任务队列中
         queueMicrotask() 用来向微任务队列中添加一个任务
           
        console.log(1);
    
        setTimeout(() => console.log(2));
    
        Promise.resolve().then(() => console.log(3));
    
        Promise.resolve().then(() => setTimeout(() => console.log(4)));
    
        Promise.resolve().then(() => console.log(5));
    
        setTimeout(() => console.log(6));
    
        console.log(7);
    // 1->7->3->5->2->6->4
    

    5.async和await#

    正常我们创建一个异步的函数是

    function fn(){
        return Promise.resolve(10)
    }
    fn().then(r=>{
        console.log(r)  //10
    })
    

    通过async可以快速的创建异步函数,是Promise的一个语法糖

    ​ 通过async创建的异步函数,异步函数的返回值会自动封装到一个Promise中返回

    ​ 在async声明异步函数中可以使用await关键字来调用异步函数

    async function fn2(){
    	return 	10
    }
    

    Promise解决了异步调用中回调函数问题

    ​ 虽然通过链式调用解决了回调地狱,但是链式调用太多以后还是不好看

    ​ 我多想以同步的方式去调用异步的代码

    1. 当我们通过await去调用异步函数时,它会暂停代码的运行
    2. 直到异步代码执行有结果时,才会将结果返回
    3. 注意 await只能用于 async声明的异步函数中,或es模块的顶级作用域中
    4. await阻塞的知识异步函数内部的代码,不会影响外部代码
    5. 通过await调用异步代码时,需要通过try-catch来处理异常
    function sum(a, b) {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve(a + b)
            }, 2000);
        })
    }
    //利用await调用
    async function fn3(){
       try {
            let result = await sum(123, 456)
            result = await sum(result, 8)
            result = await sum(result, 9)
            console.log(result)
        } catch (e) {
            console.log("出错了~~")
        }
    }
    

    执行顺序的问题:

    async function fn4() {
        console.log(1)
        /* 
            当我们使用await调用函数后,当前函数后边的所有代码
                会在当前函数执行完毕后,被放入到微任务队里中
        */
        await console.log(2)
    
        // await后边的所有代码,都会放入到微任务队列中执行
        console.log(3)
    }
    
    
  • 相关阅读:
    1、计算机组成与体系结构
    八股文学习二(spring boot + mybatis)
    『第七章』翩翩起舞的雨燕:顺序与并发执行
    SSM项目
    Python 求矩阵的局部极大值
    用友U8-Cloud upload.jsp任意文件上传漏洞
    T Chat 第八期「 龙熠 - 我在大厂做国际化 」8 月 18 日晚 8 点开播
    给出含有n个整数的数组s,找出s中和加起来的和最接近给定的目标值的三个整数。返回这三个整数的和。你可以假设每个输入都只有唯一解。
    优秀智慧园区案例 - 三亚市崖州湾科技城智慧园区,先进智慧园区建设方案经验
    数字集成电路(中)
  • 原文地址:https://www.cnblogs.com/taotaoFrontEndDevelopment/p/16948266.html