清朝云资源网

作者: 清朝云

  • 08月13日,星期二, 每天60秒读懂全世界!

    百度热搜新闻

    新闻来源:百度热搜榜

    1. 全红婵家乡连夜修路建停车场 近日,全红婵奥运夺冠带火家乡,游客打卡络绎不绝。村里连夜安排加宽路面、修建停车场。

    2. 原731部队成员抵达哈尔滨忏悔道歉 8月12日晚,原侵华日军731部队“少年队”队员清水英男抵达哈尔滨。接下来,他会到侵华日军第731部队旧址进行忏悔和道歉。

    3. 雨水增多 这些地区注意防范 8月12日至14日,华北、黄淮和西南地区东部等地降雨持续,部分地区雨势较强,公众需警惕持续降雨可能引发的各类次生灾害。

    4. 成龙发文称七小福元奎“已经离开”

    5. 上海应急局回应市民买到救灾物资 据媒体12日报道,上海一男子在二手市场买到“救灾物资”,引发关注。上海市应急管理局:本应销毁的物资被人私售,正严查。

    6. 普京:当务之急是将乌军赶出俄领土 8月12日,俄总统普京就库尔斯克州局势召开会议。普京强调,“当下最紧迫、最重要的是必须立即将乌军赶出俄罗斯领土”。

    7. 夫妻景区玩漂流1死1伤 孩子刚4岁 据媒体12日报道,江西武功山景区一游客玩漂流时不幸遇难。死者母亲:漂流时突遇涨水,女儿遇难,女婿在医院,家里孩子刚满4岁。

    8. 购买车位即可入读名校?谣言 近日,网传消息称“重庆某小区购车位可入读宏八、鲁巴等名校”,经当地有关部门核实,该信息系虚假宣传,相关学校未与该小区合作。

    9. 最高法慰问王佳佳法官家属 8月7日,河南郾城区法官王佳佳被党某某行凶报复,不幸遇害。最高人民法院党组书记、院长张军向王佳佳家属表达深切慰问。

    10. 奥运会戒断反应开始了

    —- 百度热搜新闻 End —-

    知乎新闻

    新闻来源:知乎日榜

    标题: 有哪些生物学上的事实,没有一定生物学知识的人不会相信?
    链接: https://daily.zhihu.com/story/9774538
    ———————-
    标题: 中国为啥那么多地方叫作「辛庄」,「辛庄」是什么意思?
    链接: https://daily.zhihu.com/story/9774547
    ———————-
    标题: 西周时期怎么进行分封的?
    链接: https://daily.zhihu.com/story/9774554
    ———————-
    标题: 为什么上大学后物理公式都用积分表示?
    链接: https://daily.zhihu.com/story/9774556
    ———————-
    标题: 瞎扯 · 如何正确地吐槽
    链接: https://daily.zhihu.com/story/9774557
    ———————-

    —- 知乎新闻 End —-

    IT之家新闻

    新闻来源:ITHome之家科技新闻

    标题: 周星驰经典电影《食神》时隔 28 年内地首次上映:定档 8 月 31 日,全新修复版本
    发布时间: 2024-08-12T11:20:02.9
    新闻简介: 影片由周星驰、李力持执导,周星驰、莫文蔚、薛家燕、吴孟达、谷德昭领衔主演,此次将上映首次推出的全新修复版本。
    ———————-
    标题: “小孩”曾卓君夺得 2024 电竞世界杯《街霸 6》项目冠军:8 强中有 6 个日本选手,30 万美元收入囊中
    发布时间: 2024-08-12T07:12:52.603
    新闻简介: 在刚刚结束的 2024 沙特电竞世界杯(Esports World Cup 2024,EWC2024)《街霸 6》项目总决赛上,“小孩”曾卓君 5:2 击败日本选手 KAWANO 拿下冠军,将 30 万美元(IT之家备注:当前约 215.2 万元人民币)冠军奖金收入囊中。
    ———————-
    标题: 王自如被限制高消费,此前被法院强制执行 3383 万元
    发布时间: 2024-08-12T17:42:49.987
    新闻简介: #王自如被限制高消费#的话题今日登上微博热搜,王自如因未按执行通知书指定的期间履行生效法律文书确定的给付义务,被深圳市南山区人民法院采取限制消费措施。
    ———————-
    标题: 微信自动清理 3 天未读消息引热议,官方回应“已接收内容可永久保存”
    发布时间: 2024-08-12T16:32:45.94
    新闻简介: 许多小伙伴们使用微信查阅历史消息时会遇到未读消息被清空的问题,在涉及一些重要文件场合时较为不便,今天相关话题冲上微博热门,截至IT之家发稿,相关话题阅读量 777 万,位于实时热度第 5。腾讯客服表示,在微信聊天时,已接收并下载(查看)的图片、视频、文件、文字、语音、表情等聊天记录均会被一直保存在当前设备内,可被永久查看。但出现 3 种情况可能会导致无法查看相关记录,其中一种是:未接收的消息,微信服务器会自动保留 3 天,3 天后自动清理,清理后无法再接收。
    ———————-
    标题: 博主称因拍 MEGA 车型段子被理想汽车起诉,遭索赔 100 万元
    发布时间: 2024-08-12T11:29:37.737
    新闻简介: 昨日汽车博主“董老厮说车”在抖音发布视频称,他收到了北京理想汽车有限公司在常州市武进区法院对他的起诉通知。IT之家注意到,该博主目前在抖音拥有 35.2 万粉丝。
    ———————-
    标题: AI 美女全军覆没:赛博照妖镜下集体变“鬼”,AI 代码拆台 AI 生图
    发布时间: 2024-08-12T12:37:45.127
    新闻简介: 把图像饱和度拉满,AI 人像的牙齿就会变得非常诡异,边界模糊不清。整体图片的颜色也正常,麦克风部分更是奇怪。
    ———————-
    标题: 男子打 1400 公里顺风车逃单 2800 元,哈啰回应称“将协助车主挽回损失”
    发布时间: 2024-08-12T16:10:28.92
    新闻简介: 针对“小伙打 1400 公里顺风车到后取消订单跑路”话题,哈啰顺风车今日向新京报贝壳财经记者回应称,已核实到相关订单并已限制乘客发单资质,在未归还订单费用前,其将无法用车。无论如何,都将协助车主挽回损失,也再次提醒行程双方不宜脱离平台交易。
    ———————-
    标题: 奥运冠军盛李豪使用 Find X3 发文,OPPO 送上 Find X7 Ultra
    发布时间: 2024-08-12T20:10:49.633
    新闻简介: 2024 年巴黎奥运会今日凌晨正式闭幕,本届赛事斩获两枚金牌的奥运冠军盛李豪更新微博动态,网友发现其使用的是 2021 年 3 月 11 日发布的 OPPO Find X3。随后,OPPO 官方账号闻讯而来,并在评论区称将送盛李豪一台 OPPO Find X7 Ultra 旗舰手机。
    ———————-
    标题: 龙芯中科胡伟武:3B6600 八核桌面 CPU 性能将达到英特尔中高端酷睿 12~13 代水平
    发布时间: 2024-08-12T07:53:38.497
    新闻简介: 胡伟武还透露,预计到 2024 年年底,在龙芯的 Linux 平台上可以较流畅地运行 Windows 操作系统及其应用。
    ———————-
    标题: 股价闪崩,华为终止合作?科大讯飞回应称“一直保持长期良好合作”
    发布时间: 2024-08-12T18:40:19.063
    新闻简介: 科大讯飞(-002230)股价今天上演“闪崩”,一度跌近 6%,截至收盘,跌幅收窄至 3.31%,目前该公司股票总市值为 829.91 亿人民币。对此,媒体“界面新闻”联系科大讯飞投资者关系部门,关系部门人士称,公司经营都是正常的,对于有所谓投资人称“华为很多业务要和科大讯飞终止合作”,关系部门表示“不知道从哪里看到的这个信息,我们跟你说的这家公司(华为)保持长期良好的合作”。
    ———————-
    标题: 小米有品 469 元:小米 Redmi 显示器 A24 百亿补贴 399 元
    发布时间: 2024-08-12T16:14:12.243
    新闻简介: 小米 Redmi 显示器 23.8 英寸 A24 于 2022 年 9 月 7 日发布,24 英寸 1080p 75Hz 规格,原生 8 bit 面板,首发 539 元。
    ———————-
    标题: 小米智能门锁 2 系列“旗舰新品”官宣明日发布,内嵌显示屏 / 支持人脸解锁
    发布时间: 2024-08-12T10:24:13.497
    新闻简介: 小米智能生态官微今天上午宣布,小米智能门锁2“旗舰新品”将于8月13(明)日正式登场。
    ———————-

    —- IT之家新闻 End —-

  • Vuex 使用教程


    共计 8827 个字符,预计需要花费 23 分钟才能阅读完成。

    1. 简介

    Vuex 是一个 Vue 的状态管理工具,状态就是数据。它的使用场景是多个组件共同维护一份数据(如购物车),或者多个组件使用同一个状态(个人信息)。

    其实就是集中式的管理数据,如下图所示:

    Vuex 使用教程

    />

    官方文档:https://vuex.vuejs.org/zh/

    2. 基本使用

    vue2 中使用 vuex 分为以下几个步骤:

    • 安装 vuex
    • 引入 vuex
    • 注册 vuex 插件
    • 创建 Vuex Store 实例
    • Vuex Store 注入 Vue 实例
    npm i vuex@3

    接下来要创建 Vuex Store,一般会在 src目录下新建一个 store/index.js 文件。

    // 导入 vuex
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    // vuex 也是 vue 的插件, 需要 use 一下, 进行插件的安装初始化
    Vue.use(Vuex)
    
    // 创建 Vuex Store 实例
    export default new Vuex.Store({
        state: {
          count: 1
        }
    })

    main.js 中引入 Vuex Store,并注入。

    import store from './router'
    
    new Vue({
        el: '#app',
        components: { App },
        template: '<App/>',
        store
    })

    在组件中这样使用即可:

    <template>
      <div id="app">
        <p>Count: {{ $store.state.count }}</p>
      </div>
    </template>
    
    <script>
      export default {
        name: 'HelloWorld'
      }
    </script>

    3. 核心概念

    3.1 State

    Vuex 使用单一状态树,这意味着每个应用将仅仅包含一个 store 实例。存储在 Vuex 中的数据和 Vue 实例中的 data 遵循相同的规则,例如状态对象必须是纯粹 (plain) 的。

    Vue Store 定义如下:

    // 导入 vuex
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        state: {
          count: 1
        }
    })

    组件中可以通过获取 Vuex Store 的实例 $store 获取数据。

    <template>
      <div id="app">
        <p>Count: {{ $store.state.count }}</p>
      </div>
    </template>
    
    <script>
      export default {
        name: 'HelloWorld'
      }
    </script>

    这里需要注意的是,不要将 state 数据变为 data 数据,这样会导致和其他组件状态不统一。更好的做法是,将其变成一个计算属性。

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
      </div>
    </template>
    
    <script>
      export default {
        name: 'HelloWorld',
        computed: {
          count () {
            return this.$store.state.count
          }
        }
      }
    </script>

    计算属性虽好,但是如果要获取很多个状态时,代码显得非常冗余。这个时候可以使用 mapState 辅助函数获取。

    mapState 使用数组的方式获取多个状态,方式如下:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
      </div>
    </template>
    
    <script>
      import { mapState } from 'vuex'
    
      export default {
        name: 'HelloWorld',
        computed: mapState(['count', 'num']
        //  相当于
        // count(){return this.$store.state.count}
        // num(){return this.$store.state.num}
      }
    </script>

    但是当组件有其他计算属性时,通过数组就不太合理了,这时候需要 es6 中的展开运算符:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
      </div>
    </template>
    
    <script>
      import { mapState } from 'vuex'
    
      export default {
        name: 'HelloWorld',
        data() {
          return {
            value: 17
          }
        },
        computed: {
          doubleValue () {
            return this.value * 2
          },
          ...mapState(['count','num'])
        }
      }
    </script>

    3.2 getters

    getters 相当于 vue 中的计算属性,通过 getters 进一步处理,得到想要的值,而且允许传参,第一个参数就是 state。

    Vue Store 定义如下:

    // 导入 vuex
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        state: {
          count: 1
        },
        getters: {
          doubleCount(state) {
            return state.count * 2
          }
        }
    })

    组件中使用:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
        <p>Double Count: {{ doubleCount }}</p>
      </div>
    </template>
    
    <script>
      export default {
        name: 'HelloWorld',
        computed: {
          // 上一节中提到的
          ...mapState(['count']),
          doubleCount() {
            return this.$store.getters.doubleCount
          }
        }
      }
    </script>

    还可以通过 mapGetters 辅助函数帮助我们生成计算属性:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
        <p>Double Count: {{ doubleCount }}</p>
      </div>
    </template>
    
    <script>
      import { mapState, mapGetters } from 'vuex'
    
      export default {
        name: 'HelloWorld',
        computed: {
          ...mapState(['count']),
          ...mapGetters(['doubleCount'])
        },
      }
    </script>

    3.3 mutation

    mutation 类似于 vue 中的 methods,它是更改 Vuex 的 store 中的状态的唯一方法。并且,mutation 中的方法都必须是同步的。

    每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是实际进行状态更改的地方,并且它会接受 state 作为第一个参数。

    需要注意的是,不能直接调用一个 mutation 处理函数,而需要以相应的 type 调用 store.commit 方法。

    Vue Store 定义如下:

    // 导入 vuex
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    // vuex 也是 vue 的插件, 需要 use 一下, 进行插件的安装初始化
    Vue.use(Vuex)
    
    // 创建 Vuex Store 实例
    export default new Vuex.Store({
        state: {
          count: 1
        },
        getters: {
          doubleCount(state) {
            return state.count * 2
          }
        }
        mutations: {
          increment(state) {
            state.count++
          }
        }
    })

    组件中使用:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
        <p>Double Count: {{ doubleCount }}</p>
        <button @click="increment">同步+1</button>
      </div>
    </template>
    
    <script>
      import { mapState, mapGetters } from 'vuex'
    
      export default {
        name: 'HelloWorld',
        data() {
          return {
            value: 17
          }
        },
        computed: {
          ...mapState(['count']),
          ...mapGetters(['doubleCount'])
        }, 
        methods: {
          increment() {
            this.$store.commit('increment')
          }
        }
      }
    </script>

    同样可以使用 mapMutations 简化:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
        <p>Double Count: {{ doubleCount }}</p>
        <button @click="increment">同步+1</button>
        <button @click="add">同步+1</button>
      </div>
    </template>
    
    <script>
      import { mapState, mapGetters, mapMutations } from 'vuex'
    
      export default {
        name: 'HelloWorld',
        data() {
          return {
            value: 17
          }
        },
        computed: {
          ...mapState(['count']),
          ...mapGetters(['doubleCount'])
        }, 
        methods: {
          ...mapMutations(['increment']),
          // 将 this.add() 映射为 this.$store.commit('increment')
          ...mapMutations({
            add: 'increment'
          }),
        }
      }
    </script>

    可以向 store.commit 传入额外的参数:

    // 定义
    mutations: {
      increment (state, n) {
        state.count += n
      }
    }
    // 使用
    store.commit('increment', 10)

    大多数情况下,参数应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:

    // 定义
    mutations: {
      increment (state, payload) {
        state.count += payload.amount
      }
    }
    // 使用
    store.commit('increment', {
      amount: 10
    })

    3.4 action

    Action 类似于 mutation,不同在于:

    • Action 提交的是 mutation,而不是直接变更状态。
    • Action 可以包含任意异步操作。

    action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。然而,context 并不是 store 实例本身。

    Vue Store 定义如下:

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        state: {
          count: 1
        },
        getters: {
          doubleCount(state) {
            return state.count * 2
          }
        },
        mutations: {
          increment(state) {
            state.count++
          }
        },
        actions: {
          incrementAsync(context) {
            setTimeout(() => {
              context.commit('increment')
            }, 1000)
          }
        }
    })

    组件中使用:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
        <p>Double Count: {{ doubleCount }}</p>
        <button @click="increment">同步+1</button>
        <button @click="add">同步+1</button>
        <button @click="incrementAsync">异步+1</button>
        <button @click="addAsync">异步+1</button>
      </div>
    </template>
    
    <script>
      import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
    
      export default {
        name: 'HelloWorld',
        computed: {
          ...mapState(['count']),
          ...mapGetters(['doubleCount'])
        },
        methods: {
          ...mapMutations(['increment']),
          ...mapMutations({
            add: 'increment'
          }),
          ...mapActions(['incrementAsync']),
          ...mapActions({
            addAsync: 'incrementAsync'
          })
        }
      }
    </script>

    actions 支持同样接受参数,还能支持对象形式分发:

    // 以载荷形式分发
    store.dispatch('incrementAsync', {
      amount: 10
    })
    
    // 以对象形式分发
    store.dispatch({
      type: 'incrementAsync',
      amount: 10
    })

    Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,如何才能组合多个 action,以处理更加复杂的异步流程?

    首先,需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:

    state: {
      count: 1
    },
    mutations: {
      increment(state) {
        state.count++
      }
    },
    actions: {
      incrementAsync({ commit }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            commit('increment')
            resolve()
          }, 1000)
        })
      }
    }

    现在可以使用:

    methods: {
      incrementAsync() {
        this.$store.dispatch('incrementAsync')
        .then(() => {
          console.log('incrementAsync 完成')
        })
      }
    }

    还可以:

    state: {
      count: 1
    },
    mutations: {
      increment(state) {
        state.count++
      }
    },
    actions: {
      incrementAsync({ commit }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            commit('increment')
            resolve()
          }, 1000)
        })
      },
    
      // 调用两次 increment
      incrementAsync2({ dispatch, commit }) {
        return dispatch('incrementAsync').then(() => {
          commit('increment')
        })
      }
    }

    4. 模块化开发

    4.1 简单使用

    由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

    为了解决以上问题,Vuex 允许将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    const counter = {
        state: {
            count: 0
        }
    }
    const user = {
        state: {
            name: 'zhangsan'
        }
    }
    
    const modules = {
        counter,
        user
    }
    
    export default new Vuex.Store({
        modules
    })

    在组件中使用:

    <template>
      <div id="app">
        <p>Count: {{ $store.state.counter.count }}</p>
        <p>Name: {{ $store.state.user.name }}</p>
      </div>
    </template>

    4.2 模块抽取

    其实就是将模块抽离到单独的文件中,然后通过 import 引入。

    counter.js

    const state = {
        count: 1
    }
    
    const getters = {
        doubleCount: state => state.count * 2
    }
    
    const mutations = {
        increment(state) {
            state.count++
        }
    }
    
    const actions = {
        incrementAsync(context) {
            setTimeout(() => {
                context.commit('increment')
            }, 1000)
        }
    }
    
    export default {
        namespaced: true,
        state,
        getters,
        mutations,
        actions
    }

    user.js

    const state = {
        name: "zhangsan"
    }
    
    const getters = {
        upperCaseName(state) {
            return state.name.toUpperCase()
        }
    }
    
    const mutations = {
        changeName(state) {
            state.name = 'lisi'
        }
    }
    
    const actions = {
        changeNameAsync({ commit }) {
            setTimeout(() => {
                commit("changeName")
            }, 1000)
        }
    }
    
    export default {
        namespaced: true,
        state,
        getters,
        mutations,
        actions
    }

    当在一个模块中设置了 namespaced: true 时,该模块的所有 action、mutation 和 getter 都会被限定在该命名空间下。这意味着在组件中访问这些模块的内容时,需要指定命名空间

    如果没有设置 namespaced: true,则所有模块的 action、mutation 和 getter 都会注册在全局命名空间下,可能导致命名冲突和混乱。

    store/index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    import user from './modules/user'
    import counter from './modules/counter'
    
    Vue.use(Vuex)
    
    const modules = {
        counter,
        user
    }
    
    console.log(modules)
    
    export default new Vuex.Store({
        modules
    })

    组件中使用:

    <template>
      <div id="app">
        <p>Count: {{ count }}</p>
        <p>Double Count: {{ doubleCount }}</p>
        <button @click="increment">同步+1</button>
        <button @click="incrementAsync">异步+1</button>
        <p>Name: {{ name }}</p>
        <p>UpperCase Name: {{ upperCaseName }}</p>
        <button @click="changeName">修改名称</button>
        <button @click="changeNameAsync">异步修改名称</button>
      </div>
    </template>
    
    <script>
      import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
      export default {
        name: 'HelloWorld',
        data() {
          return {
            value: 17
          }
        },
        created() {
          console.log(this.$store)
        },
        computed: {
            ...mapState({
              count: state => state.counter.count,
              name: state => state.user.name
            }),
    
            // 方式一
            // ...mapGetters('counter', [ 'doubleCount' ]),
            // ...mapGetters('user', [ 'upperCaseName' ]),
    
            // 方式二
            ...mapGetters({
              doubleCount: 'counter/doubleCount',
              upperCaseName: 'user/upperCaseName'
            })
        }, 
        methods: {
    
           // 方式一
          // ...mapMutations('counter', ['increment']),
          // ...mapMutations('user', ['changeName']),
          //...mapActions('counter', ['incrementAsync']),
          //...mapActions('user', ['changeNameAsync'])
    
          // 方式二
          ...mapMutations({
            increment: 'counter/increment',
            changeName: 'user/changeName'
          }),
          ...mapActions({
            incrementAsync: 'counter/incrementAsync',
            changeNameAsync: 'user/changeNameAsync'
          })
    
        }
      }
    </script>

    提醒:本文发布于115天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Promise 简介以及使用教程


    共计 26926 个字符,预计需要花费 68 分钟才能阅读完成。

    1. 回调地狱

    要了解 Promise 是什么,首先要知道什么是异步编程,常见的异步编程有以下几种:

    • nodejs 中 fs 模块操作磁盘文件
    • 数据库操作
    • ajax 请求
    • 定时器

    要知道,js 设计之初就是单线程模式,那么遇到异步函数不能等它执行完再执行,所以通常传递一个回调函数。例如:

    setTimeout(() => {
        console.log('3 秒后打印');
    }, 3000)

    这种方式有一个明显的问题,就是当异步操作包含异步操作时会形成回调地狱。例如:

    setTimeout(() => {
        console.log('3 秒后打印');
        setTimeout(() => {
            console.log('6 秒后打印');
            setTimeout(() => {
                console.log('9 秒后打印');
            }, 3000)
        }, 3000);
    }, 3000)

    当在项目中,大量编写这种代码是不合理的,比如登录成功以后发起请求,获取一系列数据等等。

    2. Promise简介

    Promise 即承诺的意,正如其名,它承诺未来返回给你一个处理结果。它是一种用于处理异步操作的对象,代表了一个异步操作的最终完成或失败的状态结果值。Promise 可以让你以更加清晰和可读的方式编写异步代码,避免了回调地狱的问题。

    在 Promise 中,有三种状态:

    • Pending(进行中):初始状态,既不是成功也不是失败状态。
    • Fulfilled(已成功):操作成功完成。
    • Rejected(已失败):操作失败。

    当一个 Promise 对象被创建的时候,默认就是 Pending 状态。当执行 resolve() 方法时,状态修改为 Fulfilled;当执行 reject() 方法时或抛出异常时,状态修改为 Rejected。不同的状态将会执行不同的回调函数:

    new Promise((resolve, reject) => {
        if(...){
            resolve();
        }else{
            reject();
        }
    }).then(value => {
        console.log(value)
    }, reason => {
        console.log(value)
    })

    但是其实一般会这样干,使用 catch 捕获到拒绝状态的 Promise。

    new Promise((resolve, reject) => {
        if(...){
            resolve();
        }else{
            reject();
        }
    }).then(value => {
        console.log(value)
    }).then(value => {
        console.log(value)
    }).catch((err) => {
        console.log(value)
    })

    使用 Promise 优化上面的代码:

    new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('3 秒后打印');
            resolve()
        }, 3000)
    }).then(result => {
        // 注意:这里需要返回一个 Promise 对象
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('6 秒后打印');
                resolve()
            }, 3000)
        })
    }).then(result => {
        setTimeout(() => {
            console.log('9 秒后打印');
        }, 3000)
    })

    3. Promise方法

    3.1 Promise.resolve

    Promise.resolve 返回一个已解析(resolved)的 Promise 对象,并带有一个指定的值。无论传递给 Promise.resolve 的值是一个 Promise 对象还是一个普通的值,它都会返回一个 Promise 对象,并以该值解析。

    1. 如果传递给 Promise.resolve 的是一个 Promise 对象,则它会原封不动地返回这个 Promise 对象:
    const originalPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("result");
      }, 1000);
    });
    
    const resolvedPromise = Promise.resolve(originalPromise);
    
    resolvedPromise.then(result => {
      console.log(result); // 输出: Original promise resolved
    });
    1. 如果传递给它的是一个 thenable 对象(即具有 .then 方法的对象),则 Promise.resolve 会将其视为一个 Promise 对象,并立即调用其 .then 方法:
    const thenableObject = {
      then: function(resolve, reject) {
        resolve("result");
      }
    };
    
    const resolvedFromThenable = Promise.resolve(thenableObject);
    
    resolvedFromThenable.then(result => {
      console.log(result); // 输出: result
    });
    1. 如果传递给它的是一个普通的值(比如数字、字符串等),则 Promise.resolve 会返回一个以该值解析的 Promise 对象:
    const resolvedStringPromise = Promise.resolve("result");
    
    resolvedStringPromise.then(result => {
      console.log(result); // 输出: result
    });
    
    const resolvedNumberPromise = Promise.resolve(42);
    
    resolvedNumberPromise.then(result => {
      console.log(result); // 输出: 42
    });

    3.2 Promise.reject

    Promise.reject 返回一个被拒绝(rejected)的 Promise 对象,并带有一个指定的原因(reason)。无论传递给 Promise.reject 的值是什么,它都会返回一个被拒绝的 Promise 对象,并以该值作为拒绝原因。

    const reason = "reason";
    const rejectedPromise = Promise.reject(reason);
    
    rejectedPromise.catch(error => {
      console.error('Error:', error);
    });

    即使传递的是一个字符串,还是返回一个错误的 Promise。

    3.3 Promise.all

    Promise.all 接受一个由 Promise 对象组成的可迭代对象(比如数组),并返回一个新的 Promise 对象。这个新的 Promise 对象会在可迭代对象中所有的 Promise 都已经解决(fulfilled)或至少有一个 Promise 被拒绝(rejected)后才会被解决或拒绝。

    全部成功的案例:

    const promise1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 100, 'Success-1');
    });
    
    const promise2 = new Promise((resolve, reject) => {
      setTimeout(resolve, 200, 'Success-2');
    });
    
    const promise3 = new Promise((resolve, reject) => {
      setTimeout(resolve, 300, 'Success-3');
    });
    
    const promise4 = new Promise((resolve, reject) => {
      setTimeout(reject, 150, 'Faild');
    });
    
    Promise.all([promise1, promise2, promise3])
      .then(values => {
        console.log(values); // 输出: ['Success-1', 'Success-2', 'Success-3']
      })
      .catch(error => {
        console.error('Error:', error);
      });
    
    Promise.all([promise1, promise2, promise3, promise4])
      .then(values => {
        console.log(values);
      })
      .catch(error => {
        console.error('Error:', error); // 输出: Error: Faild
      });
    

    3.4 Promise.race

    Promise.race() 接收一个 Promise 数组作为参数,并返回一个新的 Promise。这个新 Promise 将在数组中的任何一个 Promise 对象解决(resolve)或拒绝(reject)时解决或拒绝,以第一个完成的 Promise 的状态为准。

    这意味着,如果传入的 Promise 数组中有一个 Promise 对象先解决(resolve)或拒绝(reject),那么返回的 Promise 将立即采用这个解决值或拒绝原因。而其他的 Promise 对象的状态不会再影响返回的 Promise。

    const promise1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 500, 'Promise 1');
    });
    
    const promise2 = new Promise((resolve, reject) => {
      setTimeout(resolve, 1000, 'Promise 2');
    });
    
    const promise3 = new Promise((resolve, reject) => {
      setTimeout(resolve, 2000, 'Promise 3');
    });
    
    Promise.race([promise1, promise2, promise3])
      .then((value) => {
        console.log(value); // 输出 "Promise 1",因为它是第一个完成的 Promise
      });

    4. 手写Promise

    1. 初始结构搭建

    executor 为函数执行器。

    // 声明构造函数
    function Promise(executor) {
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
    
    }
    1. resolve 与 reject 结构搭建

    函数执行器需要接收两个函数作为参数,所以这两个函数需要在实例中进行定义。

    // 声明构造函数
    function Promise(executor) {
    
        // resolve 函数
        function resolve(data) {
    
        }
    
        // reject 函数
        function reject(data) {
    
        }
        // 同步调用执行器函数
        executor(resolve, reject);
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
    
    }
    1. resolve 与 reject 代码实现

    resolvereject实现的效果是修改对象的状态和结果值,但是在执行时拿到的 this 将是 Window 对象,而不是 Promise 对象。所以,需要在外面保存Promise 对象的 this 引用。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
    
        // reject 函数
        function reject(data) {
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
        // 同步调用执行器函数
        executor(resolve, reject)
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
    
    }
    1. throw抛出异常改变状态
    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
    
        // reject 函数
        function reject(data) {
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
    
    }
    1. Promise对象状态只能修改一次

    执行之前需要判断状态是否改变。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
    
        // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
    
    }
    1. then方法执行回调

    then 方法中传递两个函数,根据 Promise 的状态执行不同的函数。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
    
        // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        // 调用回调函数
        if(this.PromiseState === 'fulfilld'){
            onResolved(this.PromiseResult);
        }
        if(this.PromiseState === 'rejectd'){
            onRejected(this.PromiseResult);
        }
    
    }
    1. 异步任务回调的执行

    Promise 中的代码是同步执行的,如果里面是同步任务,那么此时立刻修改 Promise 状态,那么回调函数可以顺利执行。

    如果里面是异步任务,在 then 方法中调用回调函数时,Promise 的 状态还是 pending。这个时候应该保存一下回调函数,在执行完异步任务时再调用回调函数。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callback = {};
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 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 = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            if(self.callback.onRejected){
                self.callback.onRejected(data);
            }
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
    
        // 调用回调函数
        if(this.PromiseState === 'fulfilld'){
            onResolved(this.PromiseResult);
        }
        if(this.PromiseState === 'rejectd'){
            onRejected(this.PromiseResult);
        }
    
        if(this.PromiseState === 'pending'){
            // 保存回调函数
            this.callback = {
                onResolved,
                onRejected
            }
        }
    
    }
    1. 指定多个回调实现

    Promise 对象可以指定多个 then 方法,并且当状态改变时,then 方法都会进行调用。

    但是上面的代码实现,只会调用最后一个 then 方法,原因是保存回调函数时,只保存了最后一个 then 方法传递的回调函数。

    需要状态改变时,执行多个回调,需要将所有的回调函数都保存。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
        // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
    
        // 调用回调函数
        if(this.PromiseState === 'fulfilld'){
            onResolved(this.PromiseResult);
        }
        if(this.PromiseState === 'rejectd'){
            onRejected(this.PromiseResult);
        }
    
        if(this.PromiseState === 'pending'){
            // 保存回调函数
            this.callbacks.push({
                onResolved,
                onRejected
            })
        }
    
    }
    1. 同步修改状态then方法结果返回

    then 方法可以链式调用,说明有返回值,但是上面的代码回调并没有返回值。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                try {
                   // 获取回调函数的执行结果
                    let result = onResolved(this.PromiseResult);
                    if(result instanceof Promise){
                        result.then(v => {
                            reslove(v);
                        }, r => {
                            reject(r);
                        })
                    }else{
                        resolve(result);
                    } 
                } catch (e) {
                    reject(e);
                }
    
            }
            if(this.PromiseState === 'rejectd'){
                try {
                    // 获取回调函数的执行结果
                     let result = onRejected(this.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                } catch (e) {
                     reject(e);
                }
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved,
                    onRejected
                })
            }
        })
    }
    1. 异步修改状态then方法结果返回

    当执行 then 方法时,如果 Promise 里面执行的是异步任务,此时 Promise 状态还是 pending。那么执行 then 时,进入 if(this.PromiseState === 'pending') 判断,这里没有进行修改 Promise 的状态。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                try {
                   // 获取回调函数的执行结果
                    let result = onResolved(this.PromiseResult);
                    if(result instanceof Promise){
                        result.then(v => {
                            reslove(v);
                        }, r => {
                            reject(r);
                        })
                    }else{
                        resolve(result);
                    } 
                } catch (e) {
                    reject(e);
                }
    
            }
            if(this.PromiseState === 'rejectd'){
                try {
                    // 获取回调函数的执行结果
                     let result = onRejected(this.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                } catch (e) {
                     reject(e);
                }
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        try {
                            // 执行成功回调函数
                            let result = onResolved(self.PromiseResult);
    
                            if(result instanceof Promise){
                                result.then(v => {
                                    reslove(v);
                                }, r => {
                                    reject(r);
                                })
                            }else{
                                reslove();
                            }
                        } catch (e) {
                            reject(e);
                        }
    
                    },
                    onRejected: function() {
    
                        try {
                            // 执行回调失败函数
                            let result = onRejected(self.PromiseResult);
    
                            if(result instanceof Promise){
                                result.then(v => {
                                    reslove(v);
                                }, r => {
                                    reject(r);
                                })
                            }else{
                                reslove();
                            }
                        } catch (e) {
                            reject();
                        }      
                    }
                })
            }
        })
    }
    1. then方法优化

    上面的代码有很多是重复的,需要进行优化。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数的执行结果
                     let result = type(self.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                 } catch (e) {
                     reject(e);
                 }
            }
    
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                callback(onResolved);
            }
            if(this.PromiseState === 'rejectd'){
                callback(onRejected);
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                })
            }
        })
    }
    1. catch方法

    catch 方法只处理失败的回调函数,所以调用 then 方法即可。

    另外,如果 then 方法只传入成功的回调函数,那么失败的时候将执行不到失败的回调函数,提示 onRejected is not a function

    另外,当 then 方法什么都不传时,默认应该也能够进行值传递,所以 onResolved 应该也需要一个默认方法。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
    
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数的执行结果
                     let result = type(self.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                 } catch (e) {
                     reject(e);
                 }
            }
    
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                callback(onResolved);
            }
            if(this.PromiseState === 'rejectd'){
                callback(onRejected);
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                })
            }
        })
    }
    
    Promise.prototype.catch = function (onRejected) {
        return this.then(undefined, onRejected);
    }
    1. resolve方法封装
    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
    
        if(typeof onResolved !== 'function'){
            onResolved = value => value;
        }
    
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数的执行结果
                     let result = type(self.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                 } catch (e) {
                     reject(e);
                 }
            }
    
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                callback(onResolved);
            }
            if(this.PromiseState === 'rejectd'){
                callback(onRejected);
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                })
            }
        })
    }
    
    Promise.prototype.catch = function (onRejected) {
        return this.then(undefined, onRejected);
    }
    
    Promise.prototype.resolve = function (value) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            if(value instanceof Promise){
                value.then(v => {
                    reslove(v);
                }, r => {
                    reject(r);
                })
            }else{
                resolve(result);
            } 
        })
    }
    1. reject方法封装
    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
    
        if(typeof onResolved !== 'function'){
            onResolved = value => value;
        }
    
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数的执行结果
                     let result = type(self.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                 } catch (e) {
                     reject(e);
                 }
            }
    
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                callback(onResolved);
            }
            if(this.PromiseState === 'rejectd'){
                callback(onRejected);
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                })
            }
        })
    }
    
    Promise.prototype.catch = function (onRejected) {
        return this.then(undefined, onRejected);
    }
    
    Promise.prototype.resolve = function (value) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            if(value instanceof Promise){
                value.then(v => {
                    reslove(v);
                }, r => {
                    reject(r);
                })
            }else{
                resolve(result);
            } 
        })
    }
    
    Promise.prototype.reject = function (reason) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            reject(reason) 
        })
    }
    1. all方法封装
    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
    
        if(typeof onResolved !== 'function'){
            onResolved = value => value;
        }
    
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数的执行结果
                     let result = type(self.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                 } catch (e) {
                     reject(e);
                 }
            }
    
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                callback(onResolved);
            }
            if(this.PromiseState === 'rejectd'){
                callback(onRejected);
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                })
            }
        })
    }
    
    Promise.prototype.catch = function (onRejected) {
        return this.then(undefined, onRejected);
    }
    
    Promise.resolve = function (value) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            if(value instanceof Promise){
                value.then(v => {
                    reslove(v);
                }, r => {
                    reject(r);
                })
            }else{
                resolve(result);
            } 
        })
    }
    
    Promise.reject = function (reason) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            reject(reason) 
        })
    }
    
    Promise.all = function (promises) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
    
            let count = 0;
            let arr = [];
    
            for(let i=0;i<promises.length;i++){
                promises[i].then(v => {
                    // 得知对象的状态是成功
                    count++;
                    // 将结果存入数组
                    arr[i] = value;
                    // 成功的条件
                    if(count === promises.length){
                        resolve();
                    }
                }, r => {
                    reject(r);
                })
            }
    
        })
    }
    1. race方法封装
    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用成功的回调函数
            self.callbacks.forEach( item => {
                item.onResolved(data);
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            // 3. 调用失败的回调函数
            self.callbacks.forEach( item => {
                item.onRejected(data);
            })
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
    
        if(typeof onResolved !== 'function'){
            onResolved = value => value;
        }
    
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数的执行结果
                     let result = type(self.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                 } catch (e) {
                     reject(e);
                 }
            }
    
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                callback(onResolved);
            }
            if(this.PromiseState === 'rejectd'){
                callback(onRejected);
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                })
            }
        })
    }
    
    Promise.prototype.catch = function (onRejected) {
        return this.then(undefined, onRejected);
    }
    
    Promise.resolve = function (value) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            if(value instanceof Promise){
                value.then(v => {
                    reslove(v);
                }, r => {
                    reject(r);
                })
            }else{
                resolve(result);
            } 
        })
    }
    
    Promise.reject = function (reason) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            reject(reason) 
        })
    }
    
    Promise.all = function (promises) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
    
            let count = 0;
            let arr = [];
    
            for(let i=0;i<promises.length;i++){
                promises[i].then(v => {
                    // 得知对象的状态是成功
                    count++;
                    // 将结果存入数组
                    arr[i] = value;
                    // 成功的条件
                    if(count === promises.length){
                        resolve();
                    }
                }, r => {
                    reject(r);
                })
            }
        })
    }
    
    Promise.race = function (promises) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
    
            for(let i=0;i<promises.length;i++){
                promises[i].then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            }
        })
    }
    1. then方法回调异步执行

    then方法回调是异步执行的。

    // 声明构造函数
    function Promise(executor) {
    
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
    
        // 保存实例对象this
        const self = this;
        // resolve 函数
        function resolve(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态
            self.PromiseState = 'fulfilld';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            setTimeout(() => {
                // 3. 调用成功的回调函数
                self.callbacks.forEach( item => {
                    item.onResolved(data);
                })
            })
        }
    
         // reject 函数
        function reject(data) {
            if(self.PromiseState !== 'pending') return;
            // 1. 修改对象状态 
            self.PromiseState = 'rejectd';
            // 2. 修改对象结果值
            self.PromiseResult = data;
    
            setTimeout(() => {
                // 3. 调用失败的回调函数
                self.callbacks.forEach( item => {
                    item.onRejected(data);
                })
            }) 
        }
    
        try{
            // 同步调用执行器函数
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    
    }
    
    // 添加then方法
    Promise.prototype.then = function(onResolved, onRejected){
        const self = this;
        // 判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
    
        if(typeof onResolved !== 'function'){
            onResolved = value => value;
        }
    
        // 需要返回一个 Promise 
        return new Promise((reslove, reject) => {
            function callback(type) {
                try {
                    // 获取回调函数的执行结果
                     let result = type(self.PromiseResult);
                     if(result instanceof Promise){
                         result.then(v => {
                             reslove(v);
                         }, r => {
                             reject(r);
                         })
                     }else{
                         resolve(result);
                     } 
                 } catch (e) {
                     reject(e);
                 }
            }
    
            // 调用回调函数
            if(this.PromiseState === 'fulfilld'){
                setTimeout(() => {
                    callback(onResolved);
                });
            }
            if(this.PromiseState === 'rejectd'){
                setTimeout(() => {
                    callback(onRejected);
                });    
            }
    
            if(this.PromiseState === 'pending'){
                // 保存回调函数
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                })
            }
        })
    }
    
    Promise.prototype.catch = function (onRejected) {
        return this.then(undefined, onRejected);
    }
    
    Promise.resolve = function (value) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            if(value instanceof Promise){
                value.then(v => {
                    reslove(v);
                }, r => {
                    reject(r);
                })
            }else{
                resolve(result);
            } 
        })
    }
    
    Promise.reject = function (reason) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
            reject(reason) 
        })
    }
    
    Promise.all = function (promises) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
    
            let count = 0;
            let arr = [];
    
            for(let i=0;i<promises.length;i++){
                promises[i].then(v => {
                    // 得知对象的状态是成功
                    count++;
                    // 将结果存入数组
                    arr[i] = value;
                    // 成功的条件
                    if(count === promises.length){
                        resolve();
                    }
                }, r => {
                    reject(r);
                })
            }
        })
    }
    
    Promise.race = function (promises) {
        // 返回 Promise 对象
        return new Promise((resolve, reject) => {
    
            for(let i=0;i<promises.length;i++){
                promises[i].then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            }
        })
    }
    1. 封装成class
    class Promise{
    
        constructor(executor){
    
            this.PromiseState = 'pending';
            this.PromiseResult = null;
            this.callbacks = [];
    
            // 保存实例对象this
            const self = this;
            // resolve 函数
            function resolve(data) {
                if(self.PromiseState !== 'pending') return;
                // 1. 修改对象状态
                self.PromiseState = 'fulfilld';
                // 2. 修改对象结果值
                self.PromiseResult = data;
    
                setTimeout(() => {
                    // 3. 调用成功的回调函数
                    self.callbacks.forEach( item => {
                        item.onResolved(data);
                    })
                })
            }
    
            // reject 函数
            function reject(data) {
                if(self.PromiseState !== 'pending') return;
                // 1. 修改对象状态 
                self.PromiseState = 'rejectd';
                // 2. 修改对象结果值
                self.PromiseResult = data;
    
                setTimeout(() => {
                    // 3. 调用失败的回调函数
                    self.callbacks.forEach( item => {
                        item.onRejected(data);
                    })
                }) 
            }
    
            try{
                // 同步调用执行器函数
                executor(resolve, reject);
            }catch(e){
                reject(e);
            }
        }
    
        then(onResolved, onRejected){
            const self = this;
            // 判断回调函数参数
            if(typeof onRejected !== 'function'){
                onRejected = reason => {
                    throw reason;
                }
            }
    
            if(typeof onResolved !== 'function'){
                onResolved = value => value;
            }
    
            // 需要返回一个 Promise 
            return new Promise((reslove, reject) => {
                function callback(type) {
                    try {
                        // 获取回调函数的执行结果
                         let result = type(self.PromiseResult);
                         if(result instanceof Promise){
                             result.then(v => {
                                 reslove(v);
                             }, r => {
                                 reject(r);
                             })
                         }else{
                             resolve(result);
                         } 
                     } catch (e) {
                         reject(e);
                     }
                }
    
                // 调用回调函数
                if(this.PromiseState === 'fulfilld'){
                    setTimeout(() => {
                        callback(onResolved);
                    });
                }
                if(this.PromiseState === 'rejectd'){
                    setTimeout(() => {
                        callback(onRejected);
                    });    
                }
    
                if(this.PromiseState === 'pending'){
                    // 保存回调函数
                    this.callbacks.push({
                        onResolved: function() {
                            callback(onResolved);
                        },
                        onRejected: function() {
                            callback(onRejected);
                        }
                    })
                }
            })
        }
    
        catch(onRejected){
            return this.then(undefined, onRejected);
        }
    
        static resolve(value){
            // 返回 Promise 对象
            return new Promise((resolve, reject) => {
                if(value instanceof Promise){
                    value.then(v => {
                        reslove(v);
                    }, r => {
                        reject(r);
                    })
                }else{
                    resolve(result);
                } 
            })
        }
    
        static reject(reason){
            // 返回 Promise 对象
            return new Promise((resolve, reject) => {
                reject(reason) 
            })
        }
    
        static all(promises){
            // 返回 Promise 对象
            return new Promise((resolve, reject) => {
    
                let count = 0;
                let arr = [];
    
                for(let i=0;i<promises.length;i++){
                    promises[i].then(v => {
                        // 得知对象的状态是成功
                        count++;
                        // 将结果存入数组
                        arr[i] = value;
                        // 成功的条件
                        if(count === promises.length){
                            resolve();
                        }
                    }, r => {
                        reject(r);
                    })
                }
            })
        }
    
        static race(){
            // 返回 Promise 对象
            return new Promise((resolve, reject) => {
    
                for(let i=0;i<promises.length;i++){
                    promises[i].then(v => {
                        resolve(v);
                    }, r => {
                        reject(r);
                    })
                }
            })
        }
    }

    5. async与await

    5.1 async

    1. 函数返回值为 Promise 对象
    2. Promise 对象的结果值由 async 函数的执行的返回值决定

    5.2 await

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

    5.3 注意

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

    提醒:本文发布于114天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Axios 基本使用教程


    共计 10318 个字符,预计需要花费 26 分钟才能阅读完成。

    1. 简介

    Axios 是一个基于 promise 的网络请求库,作用于 node.js 和浏览器中。它提供了许多强大的特性,比如拦截器、请求和响应转换、取消请求等。

    具有如下特性:

    • 从浏览器发出 XMLHttpRequests
    • 从 node.js 发出 http 请求
    • 支持 Promise API
    • 拦截请求和响应
    • 转换请求和响应数据
    • 取消请求
    • JSON 数据的自动转换
    • 自动将数据对象序列化为正文编码 multipart/form-data x-www-form-urlencoded
    • 客户端支持防范 XSRF

    项目地址:https://github.com/axios/axios

    2. 安装

    可以使用 npm 安装。

    npm install axios

    或者使用 cdn 进行导入。

    <script src="https://cdn.jsdelivr.net/npm/axios@1.6.7/dist/axios.min.js"></script>

    3. 配置与基本使用

    3.1 默认配置

    以下内容为 axios 的可配置项,可以在发送请求时传递特殊

    {
      // url 是请求的服务器 URL
      url: '/user',
    
      // method 是请求使用的方法
      method: 'get', // 默认值
    
      //baseURL 会被添加到url 前面,除非url 是绝对地址。
      baseURL: 'https://some-domain.com/api/',
    
      // transformRequest 允许在发送到服务器之前修改请求数据
      // 仅适用于请求方法为 'PUT', 'POST', 'PATCH' 和 'DELETE'
      // 数组中的最后一个函数必须返回字符串或者 Buffer、ArrayBuffer、FormData 或 Stream 的实例
      // 您可以修改 headers 对象
      transformRequest: [function (data, headers) {
        // 对数据进行任何想要的转换
        return data;
      }],
    
      // transformResponse 允许在传递给 then/catch 前修改响应数据
      transformResponse: [function (data) {
        // 对数据进行任何想要的转换
        return data;
      }],
    
      // headers 是要发送的自定义头
      headers: {'X-Requested-With': 'XMLHttpRequest'},
    
      // params 是要随请求发送的 URL 参数
      // 必须是普通对象或 URLSearchParams 对象
      params: {
        ID: 12345
      },
    
      // data 是要作为请求体发送的数据
      // 仅适用于请求方法 'PUT', 'POST', 'DELETE' 和 'PATCH'
      // 当没有设置transformRequest 时,必须是以下类型之一:
      // - 字符串、普通对象、ArrayBuffer、ArrayBufferView、URLSearchParams
      // - 仅浏览器:FormData、File、Blob
      // - 仅 Node:Stream、Buffer、FormData(form-data package)
      data: {
        firstName: 'Fred'
      },
    
      // 发送数据到主体的语法替代方案
      // 方法为 post
      // 仅发送值,不发送键
      data: 'Country=Brasil&City=Belo Horizonte',
    
      //timeout 指定请求超时的毫秒数
      // 如果请求超时时间超过timeout,请求将被中止
      timeout: 1000, // 默认值为0(无超时)
    
      //responseType 表示服务器将以何种类型响应
      // 选项有:'arraybuffer'、'document'、'json'、'text'、'stream'
      // 仅浏览器:'blob'
      responseType: 'json', // 默认值
    
      //cancelToken 指定可用于取消请求的取消令牌
      // (有关详情,请参见下面的取消部分)
      cancelToken: new CancelToken(function (cancel) {
      }),
    
      // ==========================
      // 以下内容没那么常用
      // ==========================
    
      //paramsSerializer 是一个可选配置,允许您自定义序列化params。
      paramsSerializer: {
    
        // 自定义编码器函数,以迭代方式发送键值对。
        encode?: (param: string): string => { /* 在此处执行自定义操作并返回转换后的字符串 */ }, 
    
        // 用于整个参数的自定义序列化函数。允许用户模仿 1.x 之前的行为。
        serialize?: (params: Record<string, any>, options?: ParamsSerializerOptions ), 
    
        // 格式化参数中的数组索引的配置。
        indexes: false // 三个可用选项:(1) indexes: null(导致无括号),(2) (default) indexes: false(导致空括号),(3) indexes: true(导致带索引的括号)。
      },
    
      //withCredentials 表示是否应使用凭据进行跨站点访问控制请求
      withCredentials: false, // 默认值
    
      //adapter 允许自定义处理请求,从而简化测试。
      // 返回一个 Promise 并提供有效的响应(参见 lib/adapters/README.md)。
      adapter: function (config) {
        /* ... */
      },
    
      //auth 表示应该使用 HTTP 基本身份验证,并提供凭据。
      // 这将设置一个Authorization 头,覆盖任何现有的
      // 使用headers 设置的Authorization 自定义头
      // 请注意,只有 HTTP 基本身份验证可以通过此参数配置。
      // 对于 Bearer 令牌等,请改用Authorization 自定义头。
      auth: {
        username: 'janedoe',
        password: 's00pers3cret'
      },
    
      //responseEncoding 表示用于解码响应的编码(仅 Node.js)
      // 注意:对于 'stream' 或客户端请求的responseType,将被忽略
      // 选项有:'ascii'、'ASCII'、'ansi'、'ANSI'、'binary'、'BINARY'、'base64'、'BASE64'、'base64url'、
      // 'BASE64URL'、'hex'、'HEX'、'latin1'、'LATIN1'、'ucs-2'、'UCS-2'、'ucs2'、'UCS2'、'utf-8'、'UTF-8'、
      // 'utf8'、'UTF8'、'utf16le'、'UTF16LE'
      responseEncoding: 'utf8', // 默认值
    
      //xsrfCookieName 是用作 xsrf 令牌值的 cookie 的名称
      xsrfCookieName: 'XSRF-TOKEN', // 默认值
    
      //xsrfHeaderName 是携带 xsrf 令牌值的 HTTP 头的名称
      xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值
    
      //undefined(默认)- 仅为同源请求设置 XSRF 头
      withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined),
    
      //onUploadProgress 允许处理上传的进度事件
      // 浏览器和 Node.js
      onUploadProgress: function ({loaded, total, progress, bytes, estimated, rate, upload = true}) {
        // 对 Axios 进度事件进行任何操作
      },
    
      //onDownloadProgress 允许处理下载的进度事件
      // 浏览器和 Node.js
      onDownloadProgress: function ({loaded, total, progress, bytes, estimated, rate, download = true}) {
        // 对 Axios 进度事件进行任何操作
      },
    
      //maxContentLength 定义在 Node.js 中允许的 HTTP 响应内容的最大大小(以字节为单位)
      maxContentLength: 2000,
    
      //maxBodyLength(仅限 Node 选项)定义允许的 HTTP 请求内容的最大大小(以字节为单位)
      maxBodyLength: 2000,
    
      //validateStatus 定义是否应解析或拒绝给定的 HTTP 响应状态码的承诺。
      // 如果validateStatus 返回true(或设置为null 或undefined),则承诺将被解析;否则,承诺将被拒绝。
      validateStatus: function (status) {
        return status >= 200 && status < 300; // 默认值
      },
    
      //maxRedirects 定义在 Node.js 中要遵循的最大重定向次数。
      // 如果设置为 0,则不会遵循重定向。
      maxRedirects: 21, // 默认值
    
      //beforeRedirect 定义在重定向之前将调用的函数。
      // 使用此功能可以在重定向时调整请求选项,
      // 检查最新的响应头,
      // 或通过抛出错误来取消请求
      // 如果maxRedirects 设置为 0,则不使用beforeRedirect。
      beforeRedirect: (options, { headers }) => {
        if (options.hostname === "example.com") {
          options.auth = "user:password";
        }
      },
    
      //socketPath 定义要在 Node.js 中使用的 UNIX Socket。
      // 例如 '/var/run/docker.sock' 以将请求发送到 Docker 守护进程。
      // 只能指定socketPath 或proxy 其中之一。
      // 如果两者都指定,则使用socketPath。
      socketPath: null, // 默认值
    
      //transport 确定要使用的传输方法来进行请求。如果定义了,将使用它。否则,如果maxRedirects 为 0,则使用默认的http 或https 库,具体取决于protocol 中指定的协议。否则,将使用httpFollow 或httpsFollow 库,再次取决于协议,它可以处理重定向。
      transport: undefined, // 默认值
    
      //httpAgent 和httpsAgent 定义在 Node.js 中执行 http 和 https 请求时要使用的自定义代理。这允许添加选项,如keepAlive,这些选项默认情况下未启用。
      httpAgent: new http.Agent({ keepAlive: true }),
      httpsAgent: new https.Agent({ keepAlive: true }),
    
      //proxy 定义代理服务器的主机名、端口和协议。
      // 您还可以使用常规的http_proxy 和https_proxy 环境变量来定义您的代理。
      // 如果您使用环境变量进行代理配置,您还可以定义一个no_proxy 环境变量,作为不应代理的域名的逗号分隔列表。
      // 使用false 禁用代理,忽略环境变量。
      //auth 表示应该使用 HTTP 基本身份验证来连接代理,并提供凭据。
      // 这将设置一个Proxy-Authorization 头,覆盖任何现有的
      // 使用headers 设置的Proxy-Authorization 自定义头
      // 如果代理服务器使用 HTTPS,则必须将协议设置为https。
      proxy: {
        protocol: 'https',
        host: '127.0.0.1',
        // 如果定义了两者,则 'host' 优先于 'hostname'
        port: 9000,
        auth: {
          username: 'mikeymike',
          password: 'rapunz3l'
        }
      },
    
      // 使用 AbortController 取消 Axios 请求的另一种方法
      signal: new AbortController().signal,
    
      //decompress 表示是否应自动解压响应体
      // 如果设置为true,还会从所有解压的响应对象的 'content-encoding' 头中移除
      // - 仅 Node(XHR 无法关闭解压缩)
      decompress: true, // 默认值
    
      //insecureHTTPParser 布尔值。
      // 指示是否使用不安全的 HTTP 解析器接受无效的 HTTP 标头。
      // 这可能允许与不符合规范的 HTTP 实现进行交互。
      // 应避免使用不安全的解析器。
      // 有关选项,请参见 https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_url_options_callback
      // 也请参见 https://nodejs.org/en/blog/vulnerability/february-2020-security-releases/#strict-http-header-parsing-none
      insecureHTTPParser: undefined, // 默认值
    
      // 用于向后兼容的过渡选项,可能会在较新版本中删除
      transitional: {
        // 静默 JSON 解析模式
        //true  - 忽略 JSON 解析错误并将 response.data 设置为 null(旧行为)
        //false - 如果 JSON 解析失败则抛出 SyntaxError(注意:responseType 必须设置为 'json')
        silentJSONParsing: true, // 当前 Axios 版本的默认值
    
        // 即使responseType 不是 'json',也尝试解析响应字符串为 JSON
        forcedJSONParsing: true,
    
        // 在请求超时时抛出 ETIMEDOUT 错误,而不是通用的 ECONNABORTED
        clarifyTimeoutError: false,
      },
    
      env: {
        // 用于自动将 payload 序列化为 FormData 对象的 FormData 类
        FormData: window?.FormData || global?.FormData
      },
    
      formSerializer: {
          visitor: (value, key, path, helpers) => {}; // 自定义访问器函数以序列化表单值
          dots: boolean; // 使用点代替括号格式
          metaTokens: boolean; // 保留特殊结尾,如 {} 在参数键中
          indexes: boolean; // 数组索引格式 null - 无括号,false - 空括号,true - 带索引的括号
      },
    
      // 仅限 http 适配器(Node.js)
      maxRate: [
        100 * 1024, // 100KB/s 的上传限制
        100 * 1024  // 100KB/s 的下载限制
      ]
    }

    3.2 基本使用

    直接使用 Axios 函数可以通过简单的静态方法发送 HTTP 请求,而无需创建 Axios 实例。另外,axios 发送的请求都是返回一个 Promise 对象。

    • axios.request(config): 发送自定义配置的 HTTP 请求。

    • axios.get(url[, config]): 发送 GET 请求。

    • axios.post(url[, data[, config]]): 发送 POST 请求。

    • axios.patch(url[, data[, config]]): 发送 PATCH 请求。

    • axios.put(url[, data[, config]])**: 发送 PUT 请求。

    • axios.delete(url[, config]): 发送 DELETE 请求。

    • axios.head(url[, config]): 发送 HEAD 请求。

    • axios.options(url[, config]): 发送 OPTIONS 请求。

    1. axios.request

    这里直接传递一个 config 对象。

    axios.request({
        url: "http://localhost:3000/posts",
        method: "get"
    })
    .then((response) => {
        console.log(response);
    })
    .catch(error => {
        console.error(error);
    });
    
    1. axios.get
    axios.get('http://localhost:3000/posts')
        .then(response => {
            console.log(response);
        })
        .catch(error => {
            console.error(error);
        });

    如果定义 baseUrl 会让请求看起来更加简洁。

    axios.defaults.baseURL = 'http://localhost:3000';
    axios.get('/posts')
        .then(response => {
            console.log(response);
        })
        .catch(error => {
            console.error(error);
        });
    1. axios.post
    axios.defaults.baseURL = 'http://localhost:3000';
    axios.post("/posts", {
        title: "test",
        views: 1000
    })
    .then(response => {
        console.log(response);
    })
    .catch(error => {
        console.error(error);
    });

    或者将 data 定义在外面。

    axios.defaults.baseURL = 'http://localhost:3000';
    const data = {
        title: "test",
        views: 1000
    }
    
    axios.post("/posts", postData)
        .then(response => {
            console.log(response);
        })
        .catch(error => {
            console.error(error);
        });
    1. axios.put

    在 restful 风格 api 中,put 用于更新资源,并且是完全替换。

    axios.defaults.baseURL = 'http://localhost:3000';
    const data = {
        id: 1,
        title: "test",
        views: 1000
    };
    
    axios.put("/posts/1", data)
        .then(response => {
            console.log(response);
        })
        .catch(error => {
            console.error(error);
        });
    1. axios.patch

    在 restful 风格 api 中,patch 同样用于更新资源,并且是部分替换。

    axios.defaults.baseURL = 'http://localhost:3000';
    // 此时不会替换 views
    const data = {
        id: 1,
        title: "test2222222"
    };
    
    axios.patch("/posts/1", data)
        .then(response => {
            console.log(response);
        })
        .catch(error => {
            console.error(error);
        });
    1. axios.delete
    axios.defaults.baseURL = 'http://localhost:3000';
    
    axios.delete("/posts/1")
        .then(response => {
            console.log(response);
        })
        .catch(error => {
            console.error(error);
        });

    4. axios

    axios 可以使用自定义配置创建实例,例如指定不同的 baseurl 或不同的超时事件 timeout

    const instance = axios.create({
      baseURL: 'https://some-domain.com/api/',
      timeout: 1000
    });
    
    // 该接口比较特殊,需要设置较长的超时事件
    const instance2 = axios.create({
      baseURL: 'https://some-domain.com/api/',
      timeout: 30000
    });

    创建好以后和 axios 使用的方式是一样的。

    5. 拦截器

    axios 拦截器允许在请求或响应被 then 或 catch 处理之前拦截它们。这提供了一种在请求或响应被处理前,对它们进行全局的、统一的处理的方式。

    5.1 请求拦截器

    请求拦截器允许你在请求被发送之前进行一些操作,比如修改请求头部信息或添加额外的数据。例如,最常见的是在请求头添加 token:

    axios.interceptors.request.use(
      function(config) {
        // 设置 token
        const token = 'xxx'; 
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      function(error) {
        return Promise.reject(error);
      }
    );

    axios.interceptors.request.use 函数接收两个参数:一个成功的回调函数和一个失败的回调函数。成功的回调函数接收一个 config 对象作为参数,可以在这个函数中修改 config 对象的属性。失败的回调函数则接收一个错误对象作为参数,并且可以在这个函数中处理请求发送失败的情况。

    5.2 响应拦截器

    响应拦截器允许在响应被处理之前进行一些操作,比如对返回的数据进行处理或者统一处理错误信息。例如,最常见的是对没有正常返回的数据弹窗提示:

    axios.interceptors.response.use(
    
      function(config) {
        const res = response.data
    
        // 对非正常请求做处理
        if (res.code !== 20000) {
          // 做一些处理,例如弹出错误提示等
          return Promise.reject(new Error(res.message || 'Error'))
        } else {
          return res
        }
      },
      function(error) {
        return Promise.reject(error);
      }
    )

    同样,axios.interceptors.response.use 函数也接收两个参数:一个成功的回调函数和一个失败的回调函数。成功的回调函数接收一个 response 对象作为参数,可以在这个函数中处理返回的数据。失败的回调函数则接收一个错误对象作为参数,并且可以在这个函数中处理请求发送失败的情况。

    5.3 执行顺序

    请求拦截:axios 请求拦截会先执行最后指定的回调函数,依次向前面执行,先进后出

    响应拦截:axios 响应拦截会先执行最先指定的回调函数,依次向后面执行,先进先出

    import axios from 'axios';
    
    // 添加第一个请求拦截器
    axios.interceptors.request.use(config => {
        console.log('请求拦截器 1:添加 auth token');
        config.headers['Authorization'] = 'Bearer yourAuthToken';
        return config;
    }, error => {
        return Promise.reject(error);
    });
    
    // 添加第二个请求拦截器
    axios.interceptors.request.use(config => {
        console.log('请求拦截器 2:添加额外的请求头');
        config.headers['X-Extra-Header'] = 'extraValue';
        return config;
    }, error => {
        return Promise.reject(error);
    });
    
    // 添加第一个响应拦截器
    axios.interceptors.response.use(response => {
        console.log('响应拦截器 1:检查响应状态码');
        if (response.status === 200) {
            console.log('响应状态 OK');
        }
        return response;
    }, error => {
        return Promise.reject(error);
    });
    
    // 添加第二个响应拦截器
    axios.interceptors.response.use(response => {
        console.log('响应拦截器 2:转换响应数据');
        // 假设我们要转换响应数据的格式
        response.data = { transformed: true, originalData: response.data };
        return response;
    }, error => {
        return Promise.reject(error);
    });
    
    // 发起一个请求
    axios.get('https://jsonplaceholder.typicode.com/posts/1')
        .then(response => {
            console.log('转换后的响应数据:', response.data);
        })
        .catch(error => {
            console.error('请求失败:', error);
        });

    执行结果。

    请求拦截器 2:添加额外的请求头
    请求拦截器 1:添加 auth token
    响应拦截器 1:检查响应状态码
    响应状态 OK
    响应拦截器 2:转换响应数据
    转换后的响应数据: { transformed: true, originalData: ... }

    提醒:本文发布于112天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • 08月12日,星期一, 每天60秒读懂全世界!

    百度热搜新闻

    新闻来源:百度热搜榜

    1. 正直播:巴黎奥运会闭幕式

    2. 中国队40金27银24铜收官 北京时间8月11日,中国体育代表团巴黎奥运会40金27银24铜收官,创造境外参赛最好成绩!

    3. 300金长卷铺就体育强国路 8月10日,随着国乒女队斩获巴黎奥运会金牌,中国代表团夏季奥运会参赛史上第300枚金牌就此诞生。300金的背后,是坚定迈向体育强国的中国。

    4. 中国金牌榜并列第一 本届巴黎奥运会,中国体育代表团共获得40枚金牌,与美国队并列金牌榜第一。

    5. 闭幕式看到孙颖莎了 北京时间8月12日凌晨,孙颖莎亮相巴黎奥运会闭幕式!镜头一扫而过,莎莎参加闭幕式好像去春游一样。

    6. 闭幕式太抽象了 8月12日,巴黎奥运会闭幕式在法国巴黎举行。网友:闭幕式对我来说还是太抽象了!

    7. 中国代表团亮相闭幕式 北京时间8月12日,2024年巴黎奥运会闭幕式在法兰西体育场举行,中国代表团旗手李发彬、欧紫霞手持国旗入场。

    8. 雷雨天打电话易招雷劈?谣言 雷雨天用手机打电话会招雷劈吗?看看专家怎么说!

    9. 马龙:为了比赛一直忍着没换pin

    10. 法国女篮一下多了14亿人支持 8月11日,巴黎奥运会法国女篮对阵美国女篮。这场比赛冠军归属决定金牌榜排名。有网友表示:法国女篮一下多了14亿人支持。

    —- 百度热搜新闻 End —-

    知乎新闻

    新闻来源:知乎日榜

    标题: 小事 · 「他对我的爱,比我对他的爱更加无条件」
    链接: https://daily.zhihu.com/story/9774506
    ———————-
    标题: 汉初三大功臣,为什么只有军事天才韩信必须被除掉?
    链接: https://daily.zhihu.com/story/9774507
    ———————-
    标题: 为什么虾皮里总能发现小螃蟹?
    链接: https://daily.zhihu.com/story/9774514
    ———————-
    标题: 吃错东西要靠胃外翻清空,不能呕吐的动物们太难了
    链接: https://daily.zhihu.com/story/9774524
    ———————-
    标题: 法国还有贵族吗?
    链接: https://daily.zhihu.com/story/9774533
    ———————-

    —- 知乎新闻 End —-

    IT之家新闻

    新闻来源:ITHome之家科技新闻

    标题: 网友曝小米二期工厂附近挖出古墓,小米王化称“真实情况在做文勘”
    发布时间: 2024-08-11T08:59:22.583
    新闻简介: 小米公关部总经理 @王化 在微博发文对小米二期工厂附近挖出古墓传闻进行说明:假的,周营大集正在做文堪(正常勘查)。
    ———————-
    标题: 苹果 iPhone 16 / Pro 备货关键期:郑州富士康大量招工,面试 90% 都能通过
    发布时间: 2024-08-11T13:25:31.647
    新闻简介: 正值苹果 iPhone 16 系列新机备货关键期,富士康科技集团(以下简称富士康)开始大规模招聘员工。据每日经济新闻报道,8 月 8 日,富士康郑州科技园的招聘中介向记者透露,小时工的时薪上调至 26 元,返费工(需打卡 90 天)可获得 7500 元的返现奖励。相比 7 月 26 日的薪资待遇,小时工时薪增加了 2 元,返费工返现增加了 200 元。
    ———————-
    标题: 小米汽车副总裁李肖爽:SU7 在 7 月交付超 1.3 万台,超特斯拉登顶 20 万以上纯电轿车销量榜
    发布时间: 2024-08-11T10:37:10.493
    新闻简介: 小米汽车官方 8 月 1 日宣布,8 月交付量将持续破万,预计 11 月提前完成全年 10 万辆交付目标。
    ———————-
    标题: 中国缺钾究竟有多严重
    发布时间: 2024-08-11T12:09:08.497
    新闻简介: 我们经常听到一个说法,中国用全球 9% 的耕地养活了 20% 的人口,同时用掉了 33% 的化肥。潜台词就是,在有限的耕地上,正是我们的化肥工业支撑了农业的自给自足。
    ———————-
    标题: NVIDIA 4080 及以下显卡几乎无性能损失:零刻专用 EX 显卡扩展坞 299 元上市
    发布时间: 2024-08-11T15:10:24.923
    新闻简介: 零刻专用EX显卡扩展坞现已发售,内置600w电源,支持PCle x8无损外接桌面独显,单独售卖价599元,支持30天免费试玩。
    ———————-
    标题: 比亚迪宋 L DM-i 举行万人交车仪式,首周销量破万
    发布时间: 2024-08-11T10:45:27.41
    新闻简介: IT之家从比亚迪官方获悉,宋 L DM-i 万人交车暨全民众测开营盛典在比亚迪郑州超级工厂举行。比亚迪王朝网总经理路天、比亚迪汽车工程研究院副院长赵炳根,与首批宋 L DM-i 车主代表共同见证。
    ———————-
    标题: 农夫山泉创始人钟睒睒谈“中美首富”:马斯克创造科技的边界,我解决美国人没解决的农业问题
    发布时间: 2024-08-11T19:56:31.9
    新闻简介: 8 月 10 日,央视《对话》节目播出了农夫山泉股份有限公司创始人、董事长钟睒睒的访谈。节目中,钟睒睒回应了一些外界质疑。
    ———————-
    标题: 比亚迪仰望 U8 加长版谍照曝光:对标路虎揽胜行政加长版
    发布时间: 2024-08-11T11:39:10.613
    新闻简介: 迪粉之家曝光了一组疑似比亚迪仰望 U8 加长版的谍照。据悉,这款新车将弱化越野功能,主要面向城市使用,目标竞争车型为路虎揽胜行政加长版。新车将配备更加柔软的云辇 P 悬架系统,以提升乘坐舒适性。
    ———————-
    标题: 赛力斯集团:拟 13.29 亿元收购赛力斯汽车剩余 19.355% 股权,持股比例增至 100%
    发布时间: 2024-08-11T18:01:08.643
    新闻简介: 赛力斯集团 8 月 9 日晚公告,公司拟收购控股子公司赛力斯汽车少数股东金新基金持有的赛力斯汽车 19.355% 股权,交易金额约为 13.29 亿元。本次交易完成后,公司对赛力斯汽车的持股比例由 80.645% 增加至 100%,公司合并报表范围不会发生变化。
    ———————-
    标题: 消息称《黑神话:悟空》评测代码已发放,8 月 16 日解禁
    发布时间: 2024-08-11T11:24:52.977
    新闻简介: 爆料人 Tom Henderson 今日透露,备受期待的游戏《黑神话:悟空》的评测代码已经向媒体与测评人员发放,评测解禁日期定在 8 月 16 日。
    ———————-
    标题: 无缘微信手表版?华为商城更改 WATCH FIT 3 智能手表适配说明
    发布时间: 2024-08-11T14:44:09.043
    新闻简介: 官方现已修改该功能适配说明:华为 WATCH FIT 3 不支持微信手表版,无法回复微信消息。但支持微信消息提醒及文字内容查看、微信语音视频来电提醒与微信支付。
    ———————-
    标题: 央视曝光报废汽车黑产链:三小时完成发动机切割、造假、发货全流程
    发布时间: 2024-08-11T21:21:17.89
    新闻简介: 据央视《财经调查》报道,在连云港东海县循环经济产业园里,一些没有资质的“小作坊”不仅公然回收、拆解报废汽车,买卖报废汽车的“五大总成”,而且为了更好地销售报废汽车上拆下来的发动机,商家对机器上的关键身份信息“动手脚”。
    ———————-

    —- IT之家新闻 End —-

  • Java 值传递与引用传递


    共计 1396 个字符,预计需要花费 4 分钟才能阅读完成。

    Java 中方法调用参数传递类型无非两种,一种是基本数据类型、另一种是引用数据类型。受到这两种数据类型的影响,让人误以为,参数传递方式是和数据类型有关的,其实不然。无论是基本数据类型还是引用数据类型。下面我将分类阐述基本类型变量,与引用类型变量传递过程。

    1. 基本数据类型传递
    public class Test1 {
    
        public static void main(String[] args) {
            Test1 variable = new Test1();
            int x = 1;
            variable.changeValue(x);
            System.out.println("改变最终结果:" + x);
        }
    
        void changeValue(int value) {
            value++;
            System.out.println("方法内部改变结果:" + value);
        }
    }

    结果为:

    方法内部改变结果:2
    改变最终结果:1

    过程是,首先执行 int x = 1 将会在栈中存储一个1的值。在执行 void changeValue(int value) 将会进行值传递,将 x 的值赋值给 value。所以,在 changeValue 方法中改变 value 的值,并不会影响 x。

    1. 引用数据类型传递
    public class Test2 {
    
        public static void main(String[] args) {
            Test2 variable = new Test2();
            Person person = new Person();
            person.setName("zs");
            variable.changeValue(person);
            System.out.println("改变最终结果:" + person);
        }
    
        void changeValue(Person obj) {
            obj = null;
        }
    }
    
    class Person {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    结果为:

    改变最终结果:Person{name='zs'}

    这里有点怪,为什么打印的不是 null。原因在于执行 void changeValue(Person person) 方法时,传递的是 person 对象的地址值,此时 obj 也指向了 person 对象。

    但是,当运行 obj = null 时,obj 的地址值发生了改变,但 person 在堆里面的数据并不会发生改变,所以能够正常打印。

    而当方法变为如下代码时:

    public class Test2 {
    
        public static void main(String[] args) {
            Test2 variable = new Test2();
            Person person = new Person();
            person.setName("zs");
            variable.changeValue(person);
            System.out.println("改变最终结果:" + person);
        }
    
        void changeValue(Person obj) {
            obj.setName("ls");
        }
    }

    结果为:

    改变最终结果:Person{name='ls'}

    这是因为,在 changeValue 方法中,确确实实修改了堆内存里面的数据。

    提醒:本文发布于111天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Java 中 volatile 关键字


    共计 1930 个字符,预计需要花费 5 分钟才能阅读完成。

    并发编程中,有三个非常重要的概念:

    • 原子性:提供了一种互斥访问,同一时刻只能有一个线程对它进行操作

    • 可见性:一个线程对主内存对修改可以及时的被其他线程观察到

    • 有序性:一个线程观察其他线程的指令行执行顺序,由于指令重新排序的存在,该观察结果一般杂乱无序

    volatile 关键字能够保证以下两点:

    1. 可见性(Visibility):

    当一个线程修改了一个volatile变量的值时,这个新值会立即被写入到主内存中,而不会先缓存在线程的本地内存中。同时,当其他线程读取这个volatile变量时,它们会直接从主内存中读取最新的值,而不会使用本地缓存中的旧值。这样就确保了多个线程之间对共享变量的操作是可见的。

    1. 禁止指令重排序(Prevents Instruction Reordering):

    volatile关键字会禁止指令重排序优化,即在代码中volatile变量之前的操作不会被重排序到volatile变量之后,也不会被重排序到volatile变量之后的操作之前。这样可以确保指令的顺序按照程序的顺序执行,避免了可能会影响多线程程序正确性的指令重排序。

    需要注意的是,虽然volatile关键字能够保证可见性和禁止指令重排序,但它并不能保证原子性,即不保证多线程操作的原子性。

    另外,volatile 和 CAS(Compare and Swap,比较并交换)通常结合在一起使用,主要用于多线程编程中实现原子操作,特别是在并发环境下确保数据的一致性和可见性。

    关于 CAS,java.util.concurrent.atomic包提供了一系列原子操作的类,其中就包括了基于 CAS 的实现。最常见的就是java.util.concurrent.atomic.AtomicIntegerjava.util.concurrent.atomic.AtomicLong等,它们提供了诸如compareAndSet()等方法,用于在不使用锁的情况下进行原子性的操作。CAS 的使用可以提高并发性能,因为它避免了使用锁带来的线程阻塞和上下文切换。

    使用 volatile 和 CAS 来实现一个线程安全的计数器:

    import java.util.concurrent.atomic.AtomicInteger;
    
    public class VolatileCASExample {
        private volatile int counter = 0;
    
        public int getCounter() {
            return counter;
        }
    
        public void increment() {
            int currentValue;
            int newValue;
            do {
                currentValue = counter; // 获取当前值
                newValue = currentValue + 1; // 计算新值
            } while (!compareAndSwap(currentValue, newValue)); // CAS 操作,如果失败则重试
        }
    
        private synchronized boolean compareAndSwap(int expect, int update) {
            if (counter == expect) { // 检查当前值是否与期望值相等
                counter = update; // 如果相等,则更新为新值
                return true;
            }
            return false;
        }
    
        public static void main(String[] args) {
            final VolatileCASExample example = new VolatileCASExample();
            final AtomicInteger total = new AtomicInteger(0);
    
            // 创建多个线程并发增加计数器值
            Runnable task = () -> {
                for (int i = 0; i < 1000; i++) {
                    example.increment();
                    total.incrementAndGet();
                }
            };
    
            // 启动多个线程
            Thread[] threads = new Thread[10];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(task);
                threads[i].start();
            }
    
            // 等待所有线程完成
            for (Thread thread : threads) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            // 输出计数器的最终值
            System.out.println("Final counter value: " + example.getCounter());
            System.out.println("Total increments: " + total.get());
        }
    }

    提醒:本文发布于109天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Java 类的生命周期


    共计 1826 个字符,预计需要花费 5 分钟才能阅读完成。

    一个类完整的生命周期,会经历五个阶段,分别为:加载、连接、初始化、使用、和卸载。其中的连接又分为验证、准备和解析三个步骤。如下图所示:

    Java 类的生命周期

    />

    1. 加载(Loading)

    简单一句话概括,类的加载阶段就是:找到需要加载的类并把类的信息加载到 jvm 的方法区中,然后在堆区中实例化一个 java.lang.Class 对象,作为方法区中这个类的信息的入口。结合 jvm 的内存结构会比较好理解。

    Java 类的生命周期

    />

    这里的加载区别于类加载,类加载其实包括加载、连接、初始化三个阶段。类加载强调 jvm 能够直接使用所需的类,所以类必须完成初始化。

    类的加载方式比较灵活,总结下来有如下几种:

    1. 据类的全路径名找到相应的 class 文件,然后从 class 文件中读取文件内容;(常用)
    2. 从 jar 文件中读取。(常用)
    3. 从网络中获取:比如 10 年前十分流行的 Applet。
    4. 根据一定的规则实时生成,比如设计模式中的动态代理模式,就是根据相应的类自动生成它的代理类。
    5. 从非 class 文件中获取,其实这与直接从 class 文件中获取的方式本质

    2. 连接(Linking)

    连接有验证、准备、解析三个阶段。

    1. 验证

    进行类的合法性校验。会对比如字节码格式、变量与方法的合法性、数据类型的有效性、继承与实现的规范性等等进行检查,确保别加载的类能够正常的被jvm所正常运行。

    1. 准备

    为类的静态变量分配内存,并设为 jvm 默认的初值;对于非静态的变量,则不会为它们分配内存。简单说就是分内存、赋初值。注意:设置初始值为 jvm 默认初值,而不是程序设定。规则如下:

    • 基本类型(int、long、short、char、byte、boolean、float、double)的默认值为 0
    • 引用类型的默认值为 null
    • 常量的默认值为程序中设定的值,比如在程序中定义 final static int a = 100,则准备阶段中 a 的初值就是 100。
    1. 解析

    这一阶段的任务就是把常量池中的符号引用转换为直接引用。这样程序在执行时就能直接定位到所引用的类、方法或字段所对应的内存地址或偏移量,从而执行相应的操作。

    例如:

    String str = "Hello";
    int length = str.length();

    在这段代码中,str.length() 就是一个符号引用,它指向了 String类的 length() 方法。在解析阶段,Java 虚拟机将这个符号引用解析为 String 类的实际 length() 方法的直接引用,以便在执行时直接调用该方法。

    3. 初始化(Initialization)

    类初始化阶段是类加载过程的最后一步。而也是到了该阶段,才真正开始执行类中定义的 java 程序代码(字节码),之前的动作都由虚拟机主导。

    jvm对类的加载时机没有明确规范,但对类的初始化时机有:只有当类被直接引用的时候,才会触发类的初始化。类被直接引用的情况有以下几种:

    1. 通过以下几种方式:
    • new关键字创建对象
    • 读取或设置类的静态变量
    • 调用类的静态方法
    1. 通过反射方式执行里面的三种方式

    2. 初始化子类的时候,会触发父类的初始化

    3. 作为程序入口直接运行时(调用main方法)

    4. 接口实现类初始化的时候,会触发直接或间接实现的所有接口的初始化

    关于类的初始化,记住两句话:

    1. 类的初始化,会自上而下运行静态代码块或静态赋值语句,非静态与非赋值的静态语句均不执行。

    2. 如果存在父类,则父类先进行初始化,是一个典型的递归模型。

    区别于对象的初始化,类的初始化所做的一起都是基于类变量或类语句的,也就是说执行的都是共性的抽象信息。而我们知道,类就是对象实例的抽象。

    4. 使用(Using)

    类的使用分为直接引用和间接引用。

    直接引用与间接引用等判别条件,是看对该类的引用是否会引起类的初始化

    直接引用已经在类的初始化中的有过阐述,不再赘述。而类的间接引用,主要有下面几种情况:

    1. 当引用了一个类的静态变量,而该静态变量继承自父类的话,不引起初始化

    2. 定义一个类的数组,不会引起该类的初始化

    3. 当引用一个类的的常量时,不会引起该类的初始化

    5. 卸载(Unloading)

    当类使用完了之后,类就要进入卸载阶段了。那何为衡量类使用完的标准呢?

    1. 该类所有的实例都已经被回收,也就是 java 堆中不存在该类的任何实例。

    2. 加载该类的 ClassLoader 已经被回收。

    3. 该类对应的 java.lang.Class 对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

    如果以上三个条件全部满足,jvm 就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java 类的整个生命周期就结束了。

    提醒:本文发布于109天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室

  • Mysql 中几种索引失效场景分析


    共计 2357 个字符,预计需要花费 6 分钟才能阅读完成。

    表结构以及数据:

    CREATE TABLE `t1` (
      `a` INT PRIMARY KEY,
      `b` INT,
      `c` INT,
      `d` INT,
      `e` VARCHAR(20)
    ) ENGINE=InnoDB;
    
    CREATE INDEX idx_bcd ON `t1` (`b`, `c`, `d`);
    CREATE INDEX idx_e ON `t1` (`e`);
    
    insert into t1 values(4,3,1,1,'d');
    insert into t1 values(1,1,1,1,'a');
    insert into t1 values(8,8,8,8,'h');
    insert into t1 values(2,2,2,2,'b');
    insert into t1 values(5,2,3,5,'e');
    insert into t1 values(3,3,2,2,'c');
    insert into t1 values(7,4,5,5,'g');
    insert into t1 values(6,6,4,4,'f');

    索引情况:

    Mysql 中几种索引失效场景分析

    />

    a 字段是主键,对应主键索引,bcd 三个字段组成一个联合索引,e 字段一个索引。

    另外,EXPLAIN语句返回的结果集包含了查询执行计划的各个方面,这些信息有助于开发者理解查询的执行方式和性能瓶颈。下面是一些常见的EXPLAIN结果字段及其含义:

    • id: 查询的唯一标识符,用于标识查询中不同的查询块(query block)。
    • select_type: 查询的类型,如SIMPLEPRIMARYSUBQUERY等,表示查询中的子查询类型。
    • table: 此行的数据是关于哪个表的。
    • type: 表示MySQL在表中找到所需行的方式,常见的值有ALLindexrangerefeq_refconst等,从最差到最好的性能顺序排列。
    • possible_keys: 显示查询中可能使用的索引。
    • key: 实际上被查询优化器选择使用的索引。
    • key_len: 表示索引使用的字节数。
    • ref: 表示索引的哪一列被使用。
    • rows: 表示MySQL估计查询要检索的行数。
    • Extra: 包含了一些额外的信息,比如使用了哪些索引,是否使用了临时表等。

    1. 不符合最左匹配原则

    索引正常:

    explain select * from t1 where b = 1 and c = 1 and d = 1

    Mysql 中几种索引失效场景分析

    />

    索引失效

    explain select * from t1 where c = 1 and d = 1

    Mysql 中几种索引失效场景分析

    />

    由于 bcd 为联合索引,去掉了 b 就不符合最左匹配原则,索引失效。

    2. 不正确的Like查询

    不用like:

    explain select * from t1 where e = 'a'

    Mysql 中几种索引失效场景分析

    />

    正确使用like:

    explain select * from t1 where e like 'a%'

    Mysql 中几种索引失效场景分析

    />

    不正确使用like

    explain select * from t1 where e like '%a'

    Mysql 中几种索引失效场景分析

    />

    3. 对索引列进行了计算或使用了函数

    使用计算:

    explain select * from t1 where b+1 = 2

    Mysql 中几种索引失效场景分析

    />

    使用函数:

    explain select * from t1 where ifnull(b,0) = 0

    Mysql 中几种索引失效场景分析

    />

    4. 索引列进行了类型转换

    e 字段的类型是 vachar,下面这个 sql 需要把 e 字段中的字符转换成数字,会导致索引失效。

    explain select * from t1 where e = 1

    Mysql 中几种索引失效场景分析

    />

    5. <>不等于导致索引失效

    b=1 可以走索引,b<>1 就不能走索引。

    explain select * from t1 where b <> 1

    Mysql 中几种索引失效场景分析

    />

    6. order by导致索引失效

    就算利用索引,但是由于是 select * 所以需要回表,而且回表成本比较高,所以不会走索引。

    explain select * from t1 order by b,c,d

    Mysql 中几种索引失效场景分析

    />

    如果是 select b 就不需要回表了,就会选择走索引。

    explain select b from t1 order by b,c,d

    Mysql 中几种索引失效场景分析

    />

    7. 使用or导致索引失效

    正常索引:

    explain select * from t1 where b = 1

    Mysql 中几种索引失效场景分析

    />

    使用 or

    explain select * from t1 where b = 1 or c = 2

    Mysql 中几种索引失效场景分析

    />

    8. select * 导致索引失效

    使用 select *:

    explain select * from t1

    Mysql 中几种索引失效场景分析

    />

    指定列查询:

    explain select b from t1

    Mysql 中几种索引失效场景分析

    />

    9. 范围查询数据量过多导致索引失效

    新增数据:

    insert into t1 values(10,3,1,1,'d');
    insert into t1 values(20,1,1,1,'a');
    insert into t1 values(15,8,8,8,'h');
    insert into t1 values(18,2,2,2,'b');
    insert into t1 values(14,2,3,5,'e');
    insert into t1 values(13,3,2,2,'c');
    insert into t1 values(17,4,5,5,'g');
    insert into t1 values(22,6,4,4,'f');

    失效:

    explain select * from t1 where b > 1

    Mysql 中几种索引失效场景分析

    />

    索引:

    explain select * from t1 where b > 10

    Mysql 中几种索引失效场景分析

    />

    10. in范围过大

    explain select * from t1 where a in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)

    Mysql 中几种索引失效场景分析

    提醒:本文发布于108天前,文中所关联的信息可能已发生改变,请知悉!

    />

    Tips:清朝云网络工作室

  • 可重入锁 ReentrantLock 使用


    共计 1631 个字符,预计需要花费 5 分钟才能阅读完成。

    ReentrantLock可重入锁)是 Java 中的一种同步机制,它提供了与synchronized关键字相似的功能,但具有更灵活的特性。在多线程编程中,ReentrantLock允许线程在获取锁之后再次获取同一把锁,而不会导致死锁。

    ReentrantLock 具有以下特点:

    1. 可重入性:

    ReentrantLock是可重入的,这意味着一个线程可以多次获取同一把锁而不会产生死锁。当线程重复获取锁时,它必须相应地释放同样次数的锁,才能完全释放锁资源。

    1. 互斥性:

    ReentrantLock保证了线程对共享资源的互斥访问。一次只有一个线程可以持有这把锁,其他线程必须等待锁释放才能获得锁。

    1. 等待可中断:

    通过lockInterruptibly()方法,线程可以在等待锁的过程中被中断,而不是一直阻塞等待。这种特性能够避免线程长时间阻塞,提高系统的响应性。

    1. 公平性:

    ReentrantLock可以在创建时选择是否公平锁。公平锁是指锁的获取按照请求的顺序进行,而非公平锁则允许插队。公平锁会导致线程切换的开销增加,但能避免饥饿问题。

    Sychronized 和 ReentrantLock 比较:

    synchronized ReentrantLock
    Java中的一个关键字 JDK提供的一个类
    JVM层面的锁 API层面的锁
    自动加锁与释放锁 需要手动加锁与释放锁
    不可获取当前线程是否上锁 可获取当前线程是否上锁isHeldByCurrentThread
    非公平锁 公平锁或非公平锁
    不可中断 可中断 1:调用设置超时方法tryLock(long timeout ,timeUnit unit) 2:调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断
    锁的是对象,锁信息保存在对象头中 int类型的state标识来标识锁的状态
    底层有锁升级过程 没有锁升级过程

    简单实用案例:

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ReentrantLockExample {
        private final Lock lock = new ReentrantLock();
        private int count = 0;
    
        public void increment() {
            lock.lock();
            try {
                count++;
            } finally {
                lock.unlock();
            }
        }
    
        public int getCount() {
            lock.lock();
            try {
                return count;
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            ReentrantLockExample example = new ReentrantLockExample();
    
            // 创建并启动两个线程,分别增加计数器的值
            Thread thread1 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    example.increment();
                }
            });
    
            Thread thread2 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    example.increment();
                }
            });
    
            thread1.start();
            thread2.start();
    
            // 等待两个线程执行完毕
            try {
                thread1.join();
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            // 输出最终的计数器值
            System.out.println("Final Count: " + example.getCount());
        }
    }

    需要注意的是,使用ReentrantLock时,一定要确保在合适的位置释放锁,通常在finally块中释放锁以确保不管是否发生异常都能释放锁资源,避免死锁情况。

    提醒:本文发布于108天前,文中所关联的信息可能已发生改变,请知悉!

    Tips:清朝云网络工作室