论Promise在前端江湖的地位及作用

news2024/12/26 23:53:23

系列文章:

  1. 先撸清楚:并发/并行、单线程/多线程、同步/异步
  2. 论Promise在前端江湖的地位及作用

前言

上篇文章阐述了并发/并行、单线程/多线程、同步/异步等概念,这篇将会分析Promise的江湖地位。
通过本篇文章,你将了解到:

  1. 为什么需要回调?
  2. 什么是回调地狱?
  3. Promise解决了什么问题?
  4. Promise常用的API
  5. async和await 如影随形
  6. Promise的江湖地位

1. 为什么需要回调?

1.1 同步回调

先看个简单的Demo:

function add(a: number, b: number) {
    return a + b
}

function reprocess(a: number) {
    return a * a
}

function calculate() {
    //加法运算
    let sum = add(4, 5)
    //进行再处理
    let result = reprocess(sum)
    //输出最终结果
    console.log("result:", result)
}

先进行加法运算,再对运算的结果进行处理,最终输出结果。
在reprocess()函数里我们对结果进行了平方,现在想要对它进行除法操作,那么依葫芦画瓢,需要再定义一个函数:

function reprocess2(a: number) {
    return a / 2
}

再后来,还需要继续增加其它功能如减法、乘法、取模等运算,那不是要新增不少函数吗?
假设该模块的主要功能是进行加法,至于对加法结果的再加工它并不关心,外界调用者想怎么玩就怎么玩。于是,回调出现了。
我们重新设计一下代码:

//新增函数作为入参
function add(a: number, b: number, callbackFun: (sum: number) => number) {
    let sum = a + b
    return callbackFun(sum)
}

function calculate() {
    //加法运算
    let result = add(4, 5, (sum) => {
        return sum / sum
    })
    //输出最终结果
    console.log("result:", result)

    let result2 = add(6, 8, (sum) => {
        return sum * sum - sum / 2
    })
    //输出最终结果
    console.log("result2:", result2)
}

add()函数最后一个入参是函数类型的参数,调用者需要实现这个函数,我们称这个函数为回调函数。于是在calculate()函数里,我们可以针对不同的需求调用add()函数,并通过回调函数实现不同的数据加工逻辑。

calculate()函数和回调函数是在同一线程里执行,并且按照代码书写的先后顺序执行,此时的回调函数是同步回调

1.2 异步回调

假若add()函数里对数据的加工需要一定的时间,我们用setTimeout模拟一下耗时操作:

//新增函数作为入参
function add(a: number, b: number, callbackFun: (sum: number) => void) {
    setTimeout(() => {
        let sum = a + b
        callbackFun(sum)
    })
}

function calculate() {
    //加法运算
    add(4, 5, (sum) => {
        let result = sum / sum
        //输出最终结果
        console.log("result:", result)//第1个打印
    })
    console.log("calculate end...")//第2个打印
}

从打印结果看,第2个打印反而比第一个打印先出现,说明第二个打印语句先执行。
calculate()函数执行add()函数的时候,并没有一直等待回调的结果,而是立马执行了第二个打印语句,而当add()函数内部实现执行时,才会执行回调函数,虽然calculate()和回调函数在同一线程执行,但是它们并没有按照代码书写的先后顺序执行,此时的回调函数是异步回调

1.3 为什么需要它?

回调函数的出现使得代码设计更灵活。
你可能会说:异步回调我还可以理解,毕竟或多或少都会涉及到异步调用,但同步回调不是脱裤子放屁吗?
其实不然,同步回调更多的表现在灵活度上,比如我们遍历一个数组:

const score = [60, 70, 80, 90, 100]
score.forEach((value, index, array) => {
    console.log("value:", value, " index:", index)
})

forEach()函数接收的是一个同步回调函数,该函数里可以获取到数组里每一个值,并可以对它进行自定义的逻辑操作。
除了forEach()函数,同步回调还大量地被运用于其它场景。

2. 什么是回调地狱?

先看一段代码:

interface NetCallback {
    //错误返回
    error: (errMsg: string) => void
    //成功返回
    succeed: (data: object) => void
}

function fetchNetData(url: string, netCallback: NetCallback) {
    //模拟网络耗时
    setTimeout(() => {
        if (Math.random() > 0.2) {
            //成功
            netCallback.succeed({code: 200, msg: 'success'})
        } else {
            //失败
            netCallback.error(`${url} fetch error`)
        }
    }, 1000)
}

function fetchStuInfo() {
    fetchNetData('/info/stu', {
        error: (errMsg) => {
            console.log(errMsg)
        },
        succeed: (data) => {
            console.log(data)
        }
    })
}

fetchStuInfo()

上述代码是很常规的异步回调过程,看起来很正经没啥问题。
想象一种场景:通过stuId获取stuInfo,stuInfo里存有teacherId,通过teacherId获取teacherInfo,teacherInfo里有schoolId,通过schoolId获取schoolInfo。
很显然这三个接口是逐层(串行)依赖的,我们可以写出如下代码:

function fetchSchoolInfo() {
    //先获取学生信息,成功后带有teacherId
    fetchNetData('/info/stu', {
        error: (errMsg) => {
            console.log(errMsg)
        },
        succeed: (data) => {
            //通过teacherId,再获取教师信息,成功后带有schoolId
            fetchNetData('/info/teacher', {
                error: (errMsg) => {
                    console.log(errMsg)
                },
                succeed: (data) => {
                    //通过schoolId,再获取学校信息
                    fetchNetData('/info/school', {
                        error: (errMsg) => {
                            console.log(errMsg)
                        },
                        succeed: (data) => {
                            console.log(data)
                        }
                    })
                }
            })
        }
    })
}

可以看到fetchSchoolInfo()函数里嵌套地调用了fetchNetData()函数,层层递进,并且伴随着error和succeed分支判断,同时异常的错误很难抛出去。
此种场景下代码并不简洁,分支多容易出错且不易调试,当需要依赖的更多时,我们就陷入了回调地狱

3. Promise解决了什么问题?

3.1 Promise替代回调

怎么解决回调地狱的问题呢?这个时候Promise出现了。
还是以获取学生信息为例:

function fetchNetData(url: string): Promise<any> {
    //模拟网络耗时
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() > 0.2) {
                //成功
                resolve({code: 200, msg: 'success'})
            } else {
                //失败
                reject(`${url} fetch error`)
            }
        }, 1000)
    })
}

与之前的对比,fetchNetData()函数只需要传入一个参数,无需回调函数,它返回一个Promise。
当网络请求成功,则调用resolve()函数,当网络请求失败则调用reject()函数。
既然返回了Promise,接着看看如何使用这个返回值。

function fetchStuInfo() {
    fetchNetData('/info/stu').then(data => {
        //成功
        console.log(data)
    }, error => {
        //失败
        console.log(error)
    })
}

你可能会说,这看起来和使用回调的方式差不多呢,then()函数的闭包就相当于回调嘛。
确实,单看这个例子和回调差不多,接着尝试用Promise改造之前的回调地狱。

function fetchSchoolInfo() {
    //先获取学生信息,成功后带有teacherId
    fetchNetData('/info/stu')
        .then(data => fetchNetData('/info/teacher'))
        .then(data => fetchNetData('/info/school'))
        .then(data => console.log(data))
        .catch(err => console.log(err))
}

这么看,使用Promise是不是简洁了许多,回调方式代码一直往右增长,而使用Promise每个接口请求都是平铺,并且它们的逻辑关系是递进的。
三个接口都成功,则打印成功的结果。
其中一个接口失败,剩下的接口都不会再请求,并且错误结果被catch()函数捕获。

3.2 Promise基本使用

Promise 是个接口,它有两个函数:
image.png

  1. then(resolve,reject)函数,入参有两个(都是可选的),返回Promise类型
  2. catch(reject)函数,入参有一个(可选),返回Promise类型
  3. 构造Promise需要传递一个参数,其是函数类型,该函数类型包括两个入参:resolve和reject,当解决了Promise时需要调用resolve()函数,当拒绝了Promise时调用reject()函数

Promise中文意思是承诺,将Promise暴露出去意思就是将承诺放出来。

  1. 就像小明请小红帮个忙
  2. 小红不会立即帮忙,而是给小明一个承诺:我会回复你到底是帮还是不帮
  3. 小红决定帮忙:调用resolve()函数,表示这个忙我帮定了
  4. 小红决定不帮忙,调用reject()函数拒绝,表示爱莫能助
  5. 不论小红作出了什么样的答复,这个承诺就算结束了

用代码表示如下:

function helpXiaoMing(): Promise<string> {
    return new Promise((resolve, reject) => {
        //掷骰子
        if (Math.random() > 0.5) {
            resolve('这个忙我帮定了')
        } else {
            reject('爱莫能助')
        }
    })
}

无论小红resolve()还是reject(),最终小明得要知道结果。
当小明发起帮助请求时,他有两种方式可以拿到小红的回复:

  1. 一直等到小红回复,对应await()函数
  2. 先去做别的事,等小红通知,对应Promise.then()函数

我们先看第二种方式:

helpXiaoMing().then(value => {
    //成功的结果,value就是resolve的参数
    console.log(value)
}, reason => {
    //失败的结果,reason就是reject的参数
    console.log(reason)
})

从上我们也发现了Promise一个特点:无论外部是否有监听Promise结果,Promise都会按照既定逻辑更改它的状态。也就是说无论小明是否关注小红的承诺,她都需要给个准信。

回到最初的问题,Promise解决了什么问题:

  1. Promise本质上也是基于回调,只是把回调封装了
  2. Promise解决嵌套回调地狱的问题
  3. Promise使得异步代码更简洁
  4. Promise支持链式调用,很好地关联了多个异步逻辑

4. Promise常用的API

4.1 Promise 常用的API

上面列举了使用Promise基础三板斧:

  • new Promise((resolve,reject)),构造Promise对象
  • 修改状态resolve()/reject()
  • 监听(接收)Promise状态

1. then()可选参数
then()函数的两个参数都是可选的
只关注成功状态:

helpXiaoMing().then(value=>{
    console.log('success:',value)
})

只关注失败状态:

helpXiaoMing().then(null, reason => {
    console.log('fail:', reason)
})

两者皆关注:

helpXiaoMing().then(value => {
    console.log('success:', value)
}, reason => {
    console.log('fail:', reason)
})

2. catch()可选参数
不想在then里监听失败的状态,也可以单独使用catch()

helpXiaoMing().then(value => {
    console.log('success:', value)
}).catch(reason => {})

失败状态有两个来源:

  1. 显示调用了Promise.reject()函数
  2. 代码抛出了异常throw Error()

失败的状态会先找到最近能够处理该状态的地方。

3. finally()始终会执行
当Promise状态更改后,finally始终会执行,执行的顺序和书写顺序一致。

helpXiaoMing().then(value => {
    console.log('success:', value)
}).catch(reason => {
    console.log('error:', reason)
}).finally(() => {
    console.log('finally called')
})

Promise状态只要变成了成功或失败,那么finally打印将会执行,此时因为finally写在最后,因此最后执行。
交换个位置:

helpXiaoMing().finally(() => {
    console.log('finally called')
}).then(value => {
    console.log('success:', value)
}).catch(reason => {
    console.log('error:', reason)
})

finally打印先执行。

4. then()/catch()/finally() 函数返回值
这三个函数都是返回了Promise,那他们的Promise的状态由谁更改呢?

helpXiaoMing().then(value => {
    console.log('success:', value)
    return 'success occur'
}).then(value => {
    console.log('second then value:', value)
}).catch(() => {
})

第一个then()函数返回了一个Promise,而这个Promise的值就是第一个then()函数闭包里返回的 ‘success occur’。
当第二个then()执行时,会等待第一个then()函数返回的Promise状态更改,此时return 'success occur’之后就会执行Promise.resolve( ‘success occur’),因此第二个then()函数打印:second then value: success occur

同样的,当在catch()函数的闭包里返回值时,该值也作为下一个then()的入参。

helpXiaoMing().then(value => {
    console.log('success:', value)
    return 'success occur'
}).catch(() => {
    return '抓到错误,将信息传递给下一个then'
}).then(value => {
    console.log('second then value:', value)
})

至于finally(),它的闭包里没有参数,返回值也不会传递下去。

then()/catch()函数特性使得Promise可以进行链式调用。

5. then()/catch()/finally() 函数闭包返回值
理论上这几个函数的的闭包能够返回任意值,先看Promise构造函数闭包里传递的类型:

function helpXiaoMing(): Promise<any> {
    return new Promise((resolve, reject) => {
        //掷骰子
        if (Math.random() > 0.5) {
            console.log('resolve')
            //resolve('这个忙我帮定了') 返回普通字符串(基本类型)
            resolve({msg: '这个忙我帮定了'})//返回对象
        } else {
            console.log('reject')
            //reject('爱莫能助') 返回普通字符串(基本类型)
            reject({reason: '爱莫能助'})//返回对象
        }
    })
}

由上可知,传递了引用对象类型,那么helpXiaoMing().then()闭包接收的参数也是对象。而对象里比较特殊的是返回Promise类型的对象。

function helpXiaoMing(): Promise<any> {
    //外层Promise对象
    return new Promise((resolve, reject) => {
        //掷骰子
        if (Math.random() > 0.5) {
            console.log('resolve')
            //内层Promise对象
            resolve(new Promise((resolve2, reject2) => {
                setTimeout(() => {
                    resolve2('我是内部的Promise')
                }, 2000)
            }))
        } else {
            console.log('reject')
            //reject('爱莫能助') 返回普通字符串
            reject({reason: '爱莫能助'})//返回对象
        }
    })
}

当调用:

helpXiaoMing().then(value => {
    console.log('success:', value)
    return 'success occur'
})

then监听的是内层Promise对象的变化,因此最终打印的结果是:

resolve
success: 我是内部的Promise

同样的,then()/catch()/finally()闭包里也可以返回Promise对象

helpXiaoMing().then(value => {
    console.log('success:', value)
    return new Promise((resolve2, reject2) => {
        setTimeout(() => {
            resolve2('我是内部的Promise')
        }, 2000)
    })
}).then(value => {
    console.log('second then value:', value)
})

基于这种特性,Promise可作链式调用,就像最开始那会儿用Promise替代回调的写法就涉及到了Promise链式调用。

4.2 Promise 易混淆的地方

先看第一个易混点:

helpXiaoMing().then(value => {
    console.log('success:', value)
}).then(value => {
    //猜猜这里的打印结果是什么
    console.log(value)
})

如果第一个then闭包执行成功,那么第二个then闭包的结果是啥?
答案是输出:undefined
因为想要将数据往下传递,then()/catch()函数闭包里必须显式返回数据:

helpXiaoMing().then(value => {
    console.log('success:', value)
    return value
}).then(value => {
    //猜猜这里的打印结果是什么
    console.log(value)
})

当然如果是简单的表达式,那就可以忽略return:

helpXiaoMing().then(value => value).then(value => {
    //猜猜这里的打印结果是什么
    console.log(value)
})

与上面效果一致。

第二个易混点:

helpXiaoMing().then(value => {
    throw Error
}).catch()

catch()能够捕获到异常吗?
答案是:不能
catch()需要传入参数:

helpXiaoMing().then(value => {
    throw Error
}).catch(()=>{})

一个空的实现,就能捕获异常。

第三个易混点:
finally()闭包在then()或catch()闭包之后执行?
答案是:不一定
这和传统的try{…}catch{…}finally{…}不太一样,传统的先执行try里面的或者是catch里的,最终才执行finally,而此处Promise里的finally是表示该Promise状态变为了"settled",至于在then()闭包还是catch()闭包前执行,决定点在于书写的顺序,具体的Demo在上一节。

第四个易混点:
Promise需要调用then()才会触发状态变化吗?
答案是:不一定

function test() {
    return new Promise((resolve, reject) => {
        console.log('hello')
        resolve('hello')
    })
}
//没有.then,Promise状态也会变化
test()

4.3 Promise其它API

还有一些比较高级的API,如Promise.all()/Promise.allSettled()/Promise.race()/Promise.any()/Promise.reject()/Promise.resolve()等,此处就不再细说。

5. async和await 如影随形

5.1 await 返回值

Promise确实比较好用,你可能已经发现了监听Promise的状态变化是个异步的过程,then()函数里的闭包其实就是传一个回调函数进去。
有些时候我们需要等待异步任务的结果回来后再进行下一步操作,这个时候该怎么做呢?
之前提到过的Demo里,小明可以选择一直等小红的回复,也可以先去做别的事等小红的通知,第二种场景上边已经分析过了,这次我们来看看第一种场景。

async function testWait() {
    console.log('before get result')
    const result = await helpXiaoMing()
    console.log('after result:', result)
}
testWait()

使用await操作符会使得当前调用者一直等待Promise状态变为完成(可能成功、可能失败),如上第二条语句一直等到Promise结束。
如果Promise成功,则拿到具体结果,如果Promise失败则会返回异常,因此需要对await本身进行异常捕获:

async function testWait() {
    console.log('before get result')
    try {
        const result = await helpXiaoMing()
        console.log('after result:', result)
    } catch (e) {
        console.log(e)
    }
}
  1. await 作用是挂起当前线程,而不是让线程停止执行(sleep等),挂起的意思是线程执行到await 这地方就暂时不往下执行了,但它不会休息,而是先去执行其它任务
  2. 等到await 的Promise返回,线程继续执行await之后的代码
  3. await 只能在async 修饰的函数里调用

5.2 async 修饰的函数返回值

async 修饰的函数最终会返回Promise
image.png
如上图,经过async修饰的函数,它的返回值被包装为Promise对象,而该Promise对象的值来源于async 函数的return 语句,此处我们没有return,因此值类型是void。
image.png
此时Promise值类型是string。

await helpXiaoMing()发生了异常,await之后的代码不会再执行。同时async返回的Promise会调用reject()函数将异常传递出去。

async function testWait() {
    console.log('before get result')
    const result = await helpXiaoMing()
    console.log('after result:', result)
    return '完成了'
}

testWait().then(value => {
    //成功,走这
    console.log('value=>', value)
}, error => {
    //失败走这
    console.log('error=>', error)
})

5.3 理解async和await的时序

看以下例子,猜猜打印结果是什么?

function waitPromise2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('waitPromise2返回')
        }, 1000)
    })
}

async function testWait1() {
    console.log('before1 get result')
    const result = await waitPromise1()
    console.log('after1 result:', result)
    return '完成了testWait1'
}

async function testWait2() {
    console.log('before2 get result')
    const result = await waitPromise2()
    console.log('after2 result:', result)
    return '完成了testWait2'
}

testWait1()
testWait2()

答案是:

before1 get result
before2 get result
after2 result: waitPromise2返回
after1 result: waitPromise1返回

刚接触async/await 的小伙伴可能会认为:

testWait1()里不是有await 阻塞了吗?此时线程一直阻塞在await处,testWait2()没机会执行,必须等到testWait1()结束后才能执行?

而实际的效果却是:

  1. 线程执行到testWait1()里的await后挂起,并退出testWait1(),进而继续执行testWait2()
  2. 在执行testWait2()的await后也会挂起
  3. 此时testWait1()和testWait2()都执行到await了,等待各自的Promise返回结果
  4. 由于testWait2()里的await时间较短,它先完成了所以先打印了"after2 result: waitPromise2返回",紧接着testWait1()的await 也返回了

当然,如果想要testWait1()和testWait2()按顺序执行怎么办呢?
我们知道testWait1()和testWait2()都会返回Promise,我们只需要await Promise即可:

async function testWait() {
    await testWait1()
    await testWait2()
}
testWait()

其打印结果如下:

before1 get result
after1 result: waitPromise1返回
before2 get result
after2 result: waitPromise2返回

5.4 async和await 作用

Promise代表的是异步编程,而通过async和await的亲密配合,我们可以使用同步的方式编写异步的代码。
其它语言也有类似的操作,比如Koltin的协程里的withcontext()函数。

6. Promise的江湖地位

好了说了一大篇Promise,是时候总结一下了。

  1. Promise 是前端实现异步任务的基石
  2. Promise 存在于前端代码的各个方面

至于地位嘛,类比阁老
image.png

本篇介绍了Promise的基本用法以及坑点,下篇将重点分析异步任务的时序(宏任务、微任务),相信你看完再也不用担心时序问题了,敬请期待~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1688478.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法刷题笔记 数的范围(C++实现)(二分法重要例题)

文章目录 题目描述题目思路题目代码&#xff08;C&#xff09;题目感想 题目描述 给定一个按照升序排列的长度为n的整数数组&#xff0c;以及q个查询。对于每个查询&#xff0c;返回一个元素k的起始位置和终止位置&#xff08;位置从0开始计数&#xff09;。如果数组中不存在该…

Flutter 中如何优雅地使用弹框

日常开发中&#xff0c;Flutter 弹框&#xff08;Dialog&#xff09;是我们使用频率非常高的控件。无论是提示用户信息、确认用户操作&#xff0c;还是表单填写&#xff0c;弹框都能派上用场。然而&#xff0c;看似简单的弹框&#xff0c;实际使用起来却有不少坑和使用的技巧。…

轻松找回误删短信 | 超强安卓短信恢复神器

概括 我们都曾经历过不小心删除了重要消息&#xff0c;后来又后悔并认为可能无法恢复它们的情况。从技术上讲&#xff0c;该消息不会被删除&#xff1b;它会在您的 Android 手机上存储一段时间。 可以执行 Android 短信恢复&#xff0c;因为它需要一段时间才能从您的 Android…

JavaScript面试 题

1.延时加载JS有哪些方式 延时加载 :async defer 例如:<script defer type"type/javascript" srcscript.js></ script> defer:等html全部解析完成,才会执行js代码,顺次执行的 async: js和html解析是同步的,不是顺次执行js脚本(谁先加载完先执行谁)2.JS数…

黑龙江等保测评深入理解

“没有网络安全&#xff0c;就没有国家安全”&#xff0c;等级保护测评是指按照网络安全系统制定的一系列的防护过程&#xff0c;对已经有的和即将上线的商业服务的基础设施&#xff08;系统&#xff0c;数据库&#xff0c;中间件等&#xff09;所做的一系列的检查&#xff0c;…

代码随想录-Day18

513. 找树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 方法一&#xff1a;深度优先搜索 class Solution {int curVal 0;int curHeight 0;public int findBottomLeftValue(TreeNode roo…

Python筑基之旅-MySQL数据库(三)

目录 一、数据库操作 1、创建 1-1、用mysql-connector-python库 1-2、用PyMySQL库 1-3、用PeeWee库 1-4、用SQLAlchemy库 2、删除 2-1、用mysql-connector-python库 2-2、用PyMySQL库 2-3、用PeeWee库 2-4、用SQLAlchemy库 二、数据表操作 1、创建 1-1、用mysql-…

分布式理论--BASE

目录 是什么BASE 与 CAP&#xff0c;ACID 的区别BASE 和 Paxos 类共识算法的区别相关问题 是什么 BASE 理论是对 CAP 理论的进一步扩展主要强调在分布式系统中&#xff0c;为了获得更高的可用性和性能&#xff0c;可以放宽对一致性的要求&#xff0c;是对 CAP 中 AP 方案的一个…

卷爆短剧出海:五大关键,由AIGC重构

短剧高温下&#xff0c;谈谈AIGC的助攻路线。 短剧&#xff0c;一个席卷全球的高温赛道。 以往只是踏着霸总题材&#xff0c;如今&#xff0c;内容循着精品化、IP化的自然发展风向&#xff0c;给内容、制作、平台等产业全链都带来新机&#xff0c;也让短剧消费走向文化深处&am…

D60SB120-ASEMI整流桥D60SB120参数、封装、尺寸

编辑&#xff1a;ll D60SB120-ASEMI整流桥D60SB120参数、封装、尺寸 型号&#xff1a;D60SB120 品牌&#xff1a;ASEMI 封装&#xff1a;D-SB 批号&#xff1a;2024 最大重复峰值反向电压&#xff1a;1200V 最大正向平均整流电流(Vdss)&#xff1a;60A 功率(Pd)&#x…

Kubernetes 应用滚动更新

Kubernetes 应用版本号 在 Kubernetes 里&#xff0c;版本更新使用的不是 API 对象&#xff0c;而是两个命令&#xff1a;kubectl apply 和 kubectl rollout&#xff0c;当然它们也要搭配部署应用所需要的 Deployment、DaemonSet 等 YAML 文件。 在 Kubernetes 里应用都是以 …

uniapp开发vue3监听右滑返回操作,返回到指定页面

想要在uniapp框架中监听左滑或者右滑手势&#xff0c;需要使用touchstart和touchend两个api&#xff0c;因为没有原生的左右滑监听api&#xff0c;所以我们只能依靠这两个api来获取滑动开始时候的x坐标和滑动结束后的x坐标做比对&#xff0c;右滑的话&#xff0c;结束时候的x坐…

RabbitMQ(一)概述第一个应用程序

文章目录 概述AMQP和JMS官网安装开始第一个程序 概述 消息队列是实现应用程序和应用程序之间通信的中间件产品 AMQP和JMS 工作体系 官网 https://www.rabbitmq.com/ RabbitMQ是一款基于AMQP、由Erlang语言开发的消息队列产品 安装 # 拉取镜像 docker pull rabbitmq:3.13-m…

微信小程序画布显示图片绘制矩形选区

wxml <view class"page-body"><!-- 画布 --><view class"page-body-wrapper"><canvas canvas-id"myCanvas" type"2d" id"myCanvas" classmyCanvas bindtouchstart"touchStart" bindtouchmo…

UEFI EDK2源码学习(一)——环境安装

部署环境 vmvare15.0 ubuntu20.04 docker edk2 源码 具体步骤 docker安装 # 更新apt软件包索引 sudo apt-get update# 添加docker依赖 sudo apt-get install -y \apt-transport-https \ca-certificates \curl \gnupg-agent \software-properties-common# 添加docker 官方…

白嫖免费图床!CloudFlare R2太香了!

1 为啥要折腾搭建一个专属图床&#xff1f; 技术大佬写博客都用 md 格式&#xff0c;要在多平台发布&#xff0c;图片就得有外链后续如博客迁移&#xff0c;国内博客网站如掘金&#xff0c;简书&#xff0c;语雀等都做了防盗链&#xff0c;图片无法迁移 2 为啥选择CloudFlare…

力扣刷题---返回word中所有不重复的单词

当需要从一个数据集合中去除重复元素时&#xff0c;set是一个很好的选择。由于其不允许存储重复的元素&#xff0c;因此可以很容易地实现去重功能。这在处理原始数据或进行数据分析时特别有用。 题目&#xff1a; 给定一个字符串数组 words&#xff0c;请返回一个由 words 中所…

SpringCloud微服务03-微服务保护-分布式事务-MQ基础-MQ高级

一、微服务保护 1.雪崩问题 如何做好后备方案就是后续&#xff1a; 2.雪崩解决方案 某一个服务的线程是固定的&#xff0c;出现故障线程占满后&#xff0c;就不会让取调用这个服务&#xff0c;对其他服务就没有影响。 3.Sentinel ①初识Sentinel 配置过程&#xff1a;day05-服…

Unity 实现心电图波形播放(需波形图图片)

实现 在Hierarchy 面板从2D Object 中新建一个Sprite&#xff0c;将波形图图片的赋给Sprite。 修改Sprite 的Sprite Renderer 组件中Draw Mode 为Tiled, 修改Sprite Renderer 的Size 即可实现波形图播放。 在Hierarchy 面板从2D Object 中新建一个Sprite Mask 并赋以遮罩图片…

表现层框架设计之表现层设计模式_2.MVP模式

1.MVP模式 MVP&#xff08;Model-View-Presenter&#xff09;模式提供数据&#xff0c;View负责显示&#xff0c;Controller/Presenter负责逻辑的处理。MVP是从经典的模式MVC演变而来&#xff0c;它们的基本思想有相通的地方&#xff1a;Controller/Presenter负责逻辑的处理&am…