hgq's docs
主页
ES6-阮一峰 (opens new window)
Vue文档 (opens new window)
Axios文档 (opens new window)
Vue Router (opens new window)
Vuex文档 (opens new window)
面试题-Vue (opens new window)
面试题-JS (opens new window)

guoguoqiqi

漫不经心的向往
主页
ES6-阮一峰 (opens new window)
Vue文档 (opens new window)
Axios文档 (opens new window)
Vue Router (opens new window)
Vuex文档 (opens new window)
面试题-Vue (opens new window)
面试题-JS (opens new window)
  • JS数据类型
  • JS数据类型的转换
  • 判断数据类型的方法
  • null和undefined的区别
  • parseInt方法解析
  • promise简答
  • var和let以及const的区别
  • 本地存储的几种方式
  • 闭包是什么
  • 调用new的过程
  • 防抖和节流
  • 简单讲讲异步的理解
  • 简单讲讲原型和原型链
  • 判断变量的数据类型
  • 深浅拷贝
  • 数组去重的方法
  • 说一说JS中的this
  • 作用域和作用域链
  • JS的内置对象
  • JS继承的几种实现方式
  • 谈谈你对this、call、apply和bind的理解
  • 说一说原型、原型链
  • 什么是闭包,为什么使用闭包
  • JS中事件冒泡与捕获
  • 说一说JS的事件模型
  • 常用的遍历数组的方法
  • 如何创建一个Ajax
  • js 延迟加载的方式有哪些
  • 谈谈你对模块化开发的理解
  • js 的几种模块规范
  • ES6 模块与 CommonJS 模块、AMD、CMD 的差异
  • JS的运行机制、单线程、事件循环、宏任务微任务
  • arguments 的对象是什么
  • 简单介绍一下 V8 引擎的垃圾回收机制
  • 哪些操作会造成内存泄漏
  • ES6有哪些新特性
  • 说一说箭头函数
  • 什么是高阶函数
  • 为什么函数被称为一等公民
  • js的深浅拷贝
  • 函数柯里化
  • 说一说new操作符
  • 说一说对Promise的理解
    • Generator函数是什么,有什么作用
    • 说一说async和await
    • instanceof的原理是什么,如何实现
    • js 的节流与防抖
    • 相关面试题集合
    • 如何更好的处理async、await的异常
    • JS的事件委托
    • 浏览器和node中事件循环的区别
    • mouseover 和 mouseenter 的区别
    • ===与Object is的区别
    • 数组去重有哪些方法
    • 页面在浏览器中渲染出来的原理和流程
    • js和css是否会阻塞页面的渲染
    • Set、Map、WeakSet 和 WeakMap 的区别
    • 说一说Promise的实现原理
    • 说一说JS的事件循环
    • == 和 === 与隐式转化
    • 说一说回流(重排)和重绘
    • script标签中添加async或defer的作用与区别
    • 如何中断一个请求
    • 遍历一个对象身上属性的方法有哪些
    • 常用的数组方法(不改变原数组)
    • 常用的数组方法(改变原数组)
    • 常用字符串操作方法
    • 对象身上与原型相关的方法
    • 数组的reduce方法
    • 常用的位运算符有哪些
    • 浮点数运算有误差的问题
    • typeof和instanceof的区别
    • 有关js和css对页面渲染阻塞的问题
    • 说说对闭包的理解
    • DOMContentLoaded方法
    • es6中对象新增的方法
    • es6中数组新增的方法
    • es6中字符串新增的方法
    • es6新增的Reflect
    • 如何判断一个变量是否是数组
    • onload 和 DOMContentLoaded的区别
    • 大文件上传问题
    • 上传、下载和普通请求的区别
    • Javascript
    guoguoqiqi
    2022-02-21

    说一说对Promise的理解

    # Promise因何而出现

    关键词

    回调地狱

    在没有promise等解决异步的方案之前,处理异步流程一般都是采用 回调函数 。如果回调中再次进行异步操作,那么一旦嵌套很深,就会出现回调地狱的情况,这对于日后的代码维护很不利。

    例如:

    请求1(function(请求结果1){
        请求2(function(请求结果2){
            请求3(function(请求结果3){
                请求4(function(请求结果4){
                    请求5(function(请求结果5){
                        请求6(function(请求结果3){
                            ...
                        })
                    })
                })
            })
        })
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 回调函数处理异步的劣势

    • 代码臃肿。

    • 可读性差。

    • 耦合度过高,可维护性差。

    • 代码复用性差。

    • 容易滋生 bug。

    • 只能在回调里处理异常。

    # Promise是什么

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

    简单的说,从语义上来看,Promise意为承诺、答应,对应到编程中,可以理解为:将一个异步操作交给Promise,然后Promise就会承诺返回给你两个状态,‘完成‘ 或者是 ’失败(异常)‘。

    # Promise的三种状态

    等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)

    1. Promise 必须为三种状态之一,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

    2. 状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。

    3. Pending 变为 Fulfilled 会得到一个私有 value,Pending 变为 Rejected会得到一个私有 reason,当Promise达到了Fulfilled或Rejected时,执行的异步代码会接收到这个value或reason

    # 怎么使用Promise

    Promise状态只能在内部进行操作,内部操作在Promise执行器函数执行。Promise必须接受一个函数作为参数,我们称该函数为执行器函数,执行器函数又包含resolve和reject两个参数,它们是两个函数。

    • resolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)

    • reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败),并抛出错误。

    # 代码示例

    var p = new Promise((resolve, reject) => {
        // 模拟一个异步操作并且不确定结果是否正常
        console.log('Promise 执行了')
        setTimeout(() => {
            var r = Math.random()*10
            if(r > 5) {
                resolve('成功')
            } else {
                reject('异常')
            }
        }, 1000)
    })
    
    
    p.then(res => {
        console.log(res)
    }).catch(err => {
        console.error(err)
    }).finally(() => {
        console.log('无论结果如何,我都会执行')
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    执行两次的结果:

    image

    可以看到,通过Promise来执行异步操作,我们可以使用链式的语法,分别在then方法和catch方法中获取到执行成功态或者是执行失败态返回给我们的异步操作结果。即使需要再次进行异步操作也只需要继续使用链式语法,代码的可读性和维护性与回调函数相比有了极大的提高。

    并且我们可以在外层通过catch方法显式的捕获异常,对于异常处理更加的清晰。

    通过上面的示例还可以看出:Promise一旦创建就会执行

    # Promise的几个方法

    # 1. Promise.all()

    Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.all([p1, p2, p3]);
    
    1

    上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

    p的状态由p1、p2、p3决定,分成两种情况。

    • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

    • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    简单的来说,就是Promise.all()所有的参数最终都是成功态,Promise.all()返回的实例才会是成功态,并且将所有参数的结果放在一个数组中返回,只要其中有一个参数是失败态,那么Promise.all()返回的实例就会是失败态,并且结果就是那个第一次变为失败态的参数的返回结果。

    # 2. Promise.race()

    Promise.race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.race([p1, p2, p3]);
    
    1

    race,比赛的意思,这个方法的用法是,参数中率先由执行态变为成功态或者失败态的,决定了Promise.race()方法最终返回的示例的状态。

    # 3. Promise.allSettled() [ES2020]

    有时候,我们希望等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作。但是,现有的 Promise 方法很难实现这个要求。

    Promise.all()方法只适合所有异步操作都成功的情况,如果有一个操作失败,就无法满足要求。

    为了解决这个问题,ES2020 引入了Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。

    Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。

    该方法返回的新的 Promise 实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。

    const resolved = Promise.resolve(42);
    const rejected = Promise.reject(-1);
    
    const allSettledPromise = Promise.allSettled([resolved, rejected]);
    
    allSettledPromise.then(function (results) {
      console.log(results);
    });
    // [
    //    { status: 'fulfilled', value: 42 },
    //    { status: 'rejected', reason: -1 }
    // ]
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 4. Promise.any() [ES2021]

    ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。

    只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

    Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

    # 5. Promise.resolve()

    有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。

    Promise.resolve('foo')
    // 等价于
    new Promise(resolve => resolve('foo'))
    
    1
    2
    3
    • 参数是一个 Promise 实例 如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

    • 参数是一个thenable对象 thenable对象指的是具有then方法的对象。 Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。

    • 参数不是具有then()方法的对象,或根本就不是对象 如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。

    • 不带有任何参数 Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。 所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()方法。

    # 6. Promise.reject()

    Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

    const p = Promise.reject('出错了');
    // 等同于
    const p = new Promise((resolve, reject) => reject('出错了'))
    
    p.then(null, function (s) {
      console.log(s)
    });
    // 出错了
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 参考文章链接

    阮一峰ES6教程 (opens new window)
    当面试官问Promise的时候他想知道什么 (opens new window)
    面试精选之Promise (opens new window)

    ← 说一说new操作符 Generator函数是什么,有什么作用→

    最近更新
    01
    vuex数据持久化怎么做
    05-22
    02
    vue的动态路由怎么配置使用
    05-22
    03
    vue权限控制一般怎么做
    05-22
    更多文章>
    Theme by Vdoing | Copyright © 2022-2022 Guoquoqiqi | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式