从如何使用到如何实现一个Promise
前言
这篇文章我们一起来学习如何使用Promise
,以及如何实现一个自己的Promise
,讲解非常清楚,全程一步一步往后实现,附带详细注释与原理讲解。
如果你觉的这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新的文章~
promise是什么?主要用来解决什么问题?
Promise是异步编程的一种解决方案,比传统解决方案--回调函数和事件--更合理更强大。
Promise
特点:
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中),fulfilled
(已成功)和reject
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其它操作都无法改变这个状态。这也是Promise
(承诺)这个名字的由来。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。
promise主要用来解决:
- 回调地狱
- 并发请求
- 异步方案优化(但它本身不是异步的,new Promise()后,它会立即执行)
promise基本用法
ES6规定,Promise
对象是一个构造函数,用来生成Promise
实例
下面代码创造了一个Promise
实例
const promise = new Promise(function(resolve,reject){
//...
if(/*异步操作成功*/){
resolve(value)
}else{
//异步操作失败
reject(error)
}
})
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
.他们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve
函数的作用是,将Promise
对象的状态从“未完成”变成“成功”(即从pending变为resolve),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject
函数的作用是,将Promise
对象的状态从“未完成”变成“失败”(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数,或用catch
方法指定rejected
状态的回调函数。
promise.then(res=>{
//success
},error=>{
//error
}).catch(err=>{})
then
方法可以接受两个回调函数作为参数,第一个回调函数是Promise
对象的状态变为resolved
时调用,第二个回调函数是Promise
对象的状态变为rejected
时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接收Promise
对象传出的值作为参数。
Ok,通过上面对promise基本用法的描述,我们大概知道了一个promise类里面都应该包含哪些内容了:
promise状态:pending,fulfilled,rejected
promise返回值
执行器:promise执行入口(也就是你传入的那个函数)
resolve:改变promise状态为fulfilled
reject:改变promise状态为rejected
then:接收两个回调,onFulfilled, onRejected。分别在promise状态变为fulfiled或rejected后执行
catch:接受一个回调,在promise状态变为rejected后执行
简单实现一个promise
我们知道了一个promise内容至少包含以上那些内容,所以一个简单的promise内部至少是这样的
class myPromise {
static PENDING = 'pending'
static FULFILLEd = 'fulfilled'
static REJECTED = 'rejected'
constructor(init){
this.state = myPromise.PENDING // promise状态
this.promiseRes = null // promise返回值
const resolve = result=>{
//...
}
const reject = result=>{
//...
}
try{
init(resolve,reject) // init就是初始化执行器
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
//...
}
catch(onRejected){
//...
}
}
OK,大概了解之后,我们再来一个一个的看里面每个部分的实现以及作用
Promise的执行器
它其实是我们在new Promise
时传入的一个回调函数,这个函数本身是同步的,也就是说在new Promise
时它就会执行,这也是我们操作promise的入口。
class myPromise{
//...
constructor(init){
try{
init(resolve,reject) // init就是初始化执行器
}catch(err){
reject(err) //这里主要是在init执行器函数出错时,用以让promise状态变为rejected
}
}
//...
}
该函数接受两个回调函数(resolve,reject)作为参数,用以改变Promise的状态
resolve与reject方法
这两个函数作为参数传到执行器函数中,用以后续改变Promise状态
class myPromise {
static PENDING = 'pending'
static FULFILLEd = 'fulfilled'
static REJECTED = 'rejected'
constructor(init){
this.state = myPromise.PENDING // promise状态
this.promiseRes = null // promise返回值
this.resolveCallback = [] //成功回调集合
this.rejectCallback = [] //失败回调集合
const resolve = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.FULFILLEd //改变状态
this.promiseRes = result //返回值
//依次调用成功回调
this.resolveCallback.forEach(fn=>fn())
}
}
const reject = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.REJECTED //改变状态
this.promiseRes = result //返回值
// 依次调用失败回调
this.rejectCallback.forEach(fn=>fn())
}
}
try{
init(resolve,reject) // 注意this指向
}catch(err){
reject(err)
}
}
}
初步then方法
class myPromise {
static PENDING = 'pending'
static FULFILLEd = 'fulfilled'
static REJECTED = 'rejected'
constructor(init){
this.state = myPromise.PENDING // promise状态
this.promiseRes = null // promise返回值
this.resolveCallback = [] //成功回调集合
this.rejectCallback = [] //失败回调集合
const resolve = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.FULFILLEd //改变状态
this.promiseRes = result //返回值
//依次调用成功回调
this.resolveCallback.forEach(fn=>fn())
}
}
const reject = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.REJECTED //改变状态
this.promiseRes = result //返回值
// 依次调用失败回调
this.rejectCallback.forEach(fn=>fn())
}
}
try{
init(resolve,reject) // 注意this指向
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
if(this.state === myPromise.FULFILLEd && typeof onFulfilled === 'function') {
onFulfilled(this.promiseRes)
}
if(this.state === myPromise.REJECTED && typeof onRejected === 'function') {
onRejected(this.promiseRes)
}
}
}
写到这里,我们的promise已经初步成型了,我们可以来测试一下:
const res1 = new myPromise((res,rej)=>{
res('成功啦~')
rej('失败啦~')
})
res1.then((res)=>{
console.log(res)
},err=>{
console.log(err)
})
// 按照预期,这里应该是只会打印出成功啦~
从上图看我们,是不是符合我们的预期,并且myPromise
内部与原生的Promise
也是非常相似的。你们是不是觉得这里已经没问题了,上面我们只是测了一下同步方法的执行,但别忘了,Promise主要是来解决异步问题的,我们再来试一下里面执行异步方法还符不符合我们的预期?
const res1 = new myPromise((res,rej)=>{
setTimeout(()=>res('成功啦~'),1000)
// rej('失败啦~')
})
res1.then((res)=>{
console.log(res)
})
这里我们预期本来是一秒之后打印成功啦,但它并没有如我们所愿,反而是什么也没打印出来,这是因为在setTimeout执行之前(pen ding)这个then方法已经执行过了,1s后状态变成fulfilled时,then也不会再执行了。
所以我们需要保证then方法的回调函数在promise状态变成fulfilled
或rejected
时再执行,那么当promise状态为pending
时我们先要把回调存在对应的队列中,等后续状态改变后再执行
较完整then方法
OK,这里我们修改一下我们的then方法,让其保证异步代码执行的正确性 (具体实现微任务我们可以用 mutationObserver,这里我们就用setTimeout来模拟一下)
class myPromise {
static PENDING = 'pending'
static FULFILLEd = 'fulfilled'
static REJECTED = 'rejected'
constructor(init){
this.state = myPromise.PENDING // promise状态
this.promiseRes = null // promise返回值
this.resolveCallback = [] //成功回调集合
this.rejectCallback = [] //失败回调集合
const resolve = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.FULFILLEd //改变状态
this.promiseRes = result //返回值
//依次调用成功回调
this.resolveCallback.forEach(fn=>fn())
}
}
const reject = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.REJECTED //改变状态
this.promiseRes = result //返回值
// 依次调用失败回调
this.rejectCallback.forEach(fn=>fn())
}
}
try{
init(resolve,reject) // 注意this指向
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
if(this.state === myPromise.FULFILLEd && typeof onFulfilled === 'function') {
onFulfilled(this.promiseRes)
}
if(this.state === myPromise.REJECTED && typeof onRejected === 'function') {
onRejected(this.promiseRes)
}
if(this.state === myPromise.PENDING){
if(onFulfilled && typeof onFulfilled === 'function'){
this.resolveCallback.push(()=>
// 这里我们用setTimeout来模拟实现then的微任务
setTimeout(()=>{
onFulfilled(this.promiseRes)
},0)
)
}
if(onRejected && typeof onRejected === 'function'){
this.rejectCallback.push(()=>
// 这里我们用setTimeout来模拟实现then的微任务
setTimeout(()=>{
onRejected(this.promiseRes)
},0)
)
}
}
}
}
这里我们可以再测试一下上面那个异步函数的测试用例,发现它能够正确打印,OK,一个较完整的then方法就算实现了~
then的链式调用
then
方法会返回一个新的Promise
(️注意:不是原来的那个Promise
)所以可以采用链式调用
采用链式的then
,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise
对象(即有异步操作),这时后一个回调函数,就会等待该Promise
对象的状态发生变化,才会被调用。
then(onFulfilled,onRejected){
const {promiseRes,state} = this
let promise = new myPromise((reso,reje)=>{
const resolveMyPromise = promiseRes => {
try{
if(typeof onFulfilled !== 'function'){
// 如果then的第一个回调不是一个函数,直接忽略,返回一个新的promise
reso(promiseRes)
}else{
// 获取第一个回调的执行结果
const res = onFulfilled(promiseRes)
// 看该执行结果是否是一个promise
if(res instanceof myPromise){
// 是一个promise,等它状态改变后再改变then返回的promise状态
res.then(reso,rej)
}else{
// 不是一个promise,将它作为新的promise的resolve
reso(res)
}
}
}catch(err){
//异常,直接将新的promise状态置为rejected
reje(err)
}
}
const rejectMyPromise = promiseRes => {
try{
if(typeof onRejected !== 'function'){
// 如果then的第二个回调不是一个函数,直接忽略,返回一个新的promise
reje(promiseRes)
}else{
// 获取第二个回调的执行结果
const res = onRejected(promiseRes)
// 看该执行结果是否是一个promise
if(res instanceof myPromise){
// 是一个promise,等它状态改变后再改变then返回的promise状态
res.then(reso,rej)
}else{
// 不是一个promise,将它作为新的promise的resolve
reje(res)
}
}
}catch(err){
//异常,直接将新的promise状态置为rejected
reje(err)
}
}
if(state === myPromise.FULFILLEd) {
resolveMyPromise(promiseRes)
}
if(state === myPromise.REJECTED) {
rejectMyPromise(promiseRes)
}
if(state === myPromise.PENDING){
if(onFulfilled && typeof onFulfilled === 'function'){
this.resolveCallback.push(()=>
// 这里我们用setTimeout来模拟实现then的微任务
setTimeout(()=>{
resolveMyPromise(this.promiseRes)
},0)
)
}
if(onRejected && typeof onRejected === 'function'){
this.rejectCallback.push(()=>
// 这里我们用setTimeout来模拟实现then的微任务
setTimeout(()=>{
rejectMyPromise(this.promiseRes)
},0)
)
}
}
})
return promise
}
catch方法
我们知道then的第二个回调其实与catch方法是一样的,所以catch方法我们可以这样实现
catch(onRejected) {
return this.then(undefined,onRejected)
}
Promise.resolve
将对象转为一个promise对象,根据参数不通可分为四种情况
- 参数是一个Promise实例,直接返回该实例
- 参数是一个
thenable
对象,将该对象转为Promise对象后,执行该对象的then
方法 - 没有参数,也是返回一个状态为
resolved
的新的Promise对象 - 参数是一个一个原始值,返回一个新的Promise对象,状态为
resolved
手动实现:
static resolve(v){
//1.参数是一个Promise实例,直接返回
if(v instanceof myPromise){
return v
}
//2.参数是一个thenable对象,转为Promise后执行该对象的then方法
if(typeof v === 'object' && typeof v.then === 'function'){
return new myPromise((res,rej)=>{
v.then(res,rej)
})
}
//3.没有参数,直接返回一个resolved状态的promise
if(!v){
return new myPromise(res=>{
res()
})
}
//4.参数是一个原始值,返回一个新的Promise,状态为resolved
return new myPromise(res=>{
res(v)
})
}
Promise.reject
返回一个新的Promise对象,状态为rejected
static reject(v){
return new myPromise((res,rej)=>{
rej(v)
})
}
Promise.all
该方法用于将多个Promise实例包装成一个新的Promise实例,如果有不是Promise的项,则让该项直接成功
用法:
const p = Promise.all([p1,p2,p3])
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
Ok,了解完Promise.all
我们动手来实现一遍
手动实现:
static all (promises){
return new myPromise((res,rej)=>{
let count = 0
const result = [];
function addFun(index,resf) {
result[index]=resf // 这里用索引别用push,保证返回的顺序
count++
if(count==promises.length) {
res(result)
}
}
[].forEach.call(promises,(promise,index)=>{
if(promise instanceof myPromise) {
promise.then(success=>{
// count ++
// result.push(success)
addFun(index,success)
},err=>{
rej(err)
})
}else{
addFun(index,promise)
}
})
})
}
Promise.race
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
用法:
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
手动实现:
static race(promises) {
return new myPromise((res,rej)=>{
[].forEach.call(promises,promise=>{
if(promise instanceof myPromise){
promise.then(success=>{
res(success)
},error=>{
rej(error)
})
}else{
res(promise)
}
})
})
}
完整代码
class myPromise {
static PENDING = 'pending'
static FULFILLEd = 'fulfilled'
static REJECTED = 'rejected'
constructor(init){
this.state = myPromise.PENDING // promise状态
this.promiseRes = null // promise返回值
this.resolveCallback = [] //成功回调集合
this.rejectCallback = [] //失败回调集合
const resolve = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.FULFILLEd //改变状态
this.promiseRes = result //返回值
//依次调用成功回调
this.resolveCallback.forEach(fn=>fn())
}
}
const reject = result=>{
// 只有当状态为pending时才改变,保证状态一旦改变就不会再变
if(this.state === myPromise.PENDING){
this.state = myPromise.REJECTED //改变状态
this.promiseRes = result //返回值
// 依次调用失败回调
this.rejectCallback.forEach(fn=>fn())
}
}
try{
init(resolve,reject) // 注意this指向
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
const {promiseRes,state} = this
let promise = new myPromise((reso,reje)=>{
const resolveMyPromise = promiseRes => {
try{
if(typeof onFulfilled !== 'function'){
// 如果then的第一个回调不是一个函数,直接忽略,返回一个新的promise
reso(promiseRes)
}else{
// 获取第一个回调的执行结果
const res = onFulfilled(promiseRes)
// 看该执行结果是否是一个promise
if(res instanceof myPromise){
// 是一个promise,等它状态改变后再改变then返回的promise状态
res.then(reso,rej)
}else{
// 不是一个promise,将它作为新的promise的resolve
reso(res)
}
}
}catch(err){
//异常,直接将新的promise状态置为rejected
reje(err)
}
}
const rejectMyPromise = promiseRes => {
try{
if(typeof onRejected !== 'function'){
// 如果then的第二个回调不是一个函数,直接忽略,返回一个新的promise
reje(promiseRes)
}else{
// 获取第二个回调的执行结果
const res = onRejected(promiseRes)
// 看该执行结果是否是一个promise
if(res instanceof myPromise){
// 是一个promise,等它状态改变后再改变then返回的promise状态
res.then(reso,rej)
}else{
// 不是一个promise,将它作为新的promise的resolve
reje(res)
}
}
}catch(err){
//异常,直接将新的promise状态置为rejected
reje(err)
}
}
if(state === myPromise.FULFILLEd) {
resolveMyPromise(promiseRes)
}
if(state === myPromise.REJECTED) {
rejectMyPromise(promiseRes)
}
if(state === myPromise.PENDING){
if(onFulfilled && typeof onFulfilled === 'function'){
this.resolveCallback.push(()=>
// 这里我们用setTimeout来模拟实现then的微任务
setTimeout(()=>{
resolveMyPromise(this.promiseRes)
},0)
)
}
if(onRejected && typeof onRejected === 'function'){
this.rejectCallback.push(()=>
// 这里我们用setTimeout来模拟实现then的微任务
setTimeout(()=>{
rejectMyPromise(this.promiseRes)
},0)
)
}
}
})
return promise
}
catch(onRejected) {
return this.then(undefined,onRejected)
}
static all (promises){
return new myPromise((res,rej)=>{
let count = 0
const result = [];
function addFun(index,resf) {
result[index]=resf // 这里用索引别用push,保证返回的顺序
count++
if(count==promises.length) {
res(result)
}
}
[].forEach.call(promises,(promise,index)=>{
if(promise instanceof myPromise) {
promise.then(success=>{
addFun(index,success)
},err=>{
rej(err)
})
}else{
addFun(index,promise)
}
})
})
}
static race(promises) {
return new myPromise((res,rej)=>{
[].forEach.call(promises,promise=>{
if(promise instanceof myPromise){
promise.then(success=>{
res(success)
},error=>{
rej(error)
})
}else{
res(promise)
}
})
})
}
static resolve(v){
//1.参数是一个Promise实例,直接返回
if(v instanceof myPromise){
return v
}
//2.参数是一个thenable对象,转为Promise后执行该对象的then方法
if(typeof v === 'object' && typeof v.then === 'function'){
return new myPromise((res,rej)=>{
v.then(res,rej)
})
}
//3.没有参数,直接返回一个resolved状态的promise
if(!v){
return new myPromise(res=>{
res()
})
}
//4.参数是一个原始值,返回一个新的Promise,状态为resolved
return new myPromise(res=>{
res(v)
})
}
static reject(v){
return new myPromise((res,rej)=>{
rej(v)
})
}
}
总结
OK,上面跟大家一起过了一遍Promise
的用法以及自己动手实现了一遍Promise
,想必看完这篇文章,大家对Promise
会有一个更加清晰的认识。
我是南玖
,感谢各位的:「点赞和关注」,我们下期见!
从如何使用到如何实现一个Promise的更多相关文章
- 教你一步一步实现一个Promise
Promise我想现在大家都非常熟悉了,主要作用就是解决异步回调问题,这里简单介绍下. Promise规范是CommonJS规范之一,而Promise规范又分了好多种,比如 Promises/A.Pr ...
- 【原】手写一个promise
上一篇文章中,我们介绍了Promise的基本使用,在这篇文章中,我们试着自己来写一个Promise,主要是学习Promise的内部机制,学习它的编程思想. !!!备注:本文写的不好,仅供自己学习之用, ...
- [翻译]简单的实现一个Promise
英文原文为:https://www.promisejs.org/implementing/ 1. 状态机 因为 promise 对象是一个状态机,所以我们首先应该定义将要用到的状态. var PEND ...
- Promise原理—一步一步实现一个Promise
promise特点 一个promise的当前状态只能是pending.fulfilled和rejected三种之一.状态改变只能是pending到fulfilled或者pending到rejected ...
- Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)
1.什么是Promise? Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一 2.对于几种常见异步编程方案 回调函数 事件监听 发布/ ...
- 面试----你可以手写一个promise吗
参考:https://www.jianshu.com/p/473cd754311f <!DOCTYPE html> <html> <head> <meta c ...
- promise对象的回调函数resolve的参数为另一个promise对象
/*如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数. reject函数的参数通常是Error对象的实例,表示抛出的错误: resolve函数的参数除了正常的值 ...
- 自己实现一个Promise库
源码地址 先看基本使用 const promise = new Promise((resolve, reject) => { resolve(value) // or reject(reason ...
- 实现一个Promise.all
用js自己实现一个Promise.all let promiseAll = (promises) => { return new Promise((resolve, reject) => ...
- 掘金转载-手写一个Promise
目录 一 什么是Promise ? 二 Promises/A+ 规范 2.1 术语 2.2 基本要求 2.2.1. Promise的状态 2.2.2. Then 方法 2.3 简易版实践 2.4 进一 ...
随机推荐
- 车载以太网第二弹|测试之实锤-AVB测试实践
背景 AVB(Audio Video Bridging)音视频桥接,是由IEEE 802.1标准委员会的IEEE AVB任务组制定的一组技术标准,包括精确时钟同步.带宽预留和流量调度等协议规范,用于构 ...
- 虚拟机快照和linux基础命令
虚拟机快照 磁盘"快照"是虚拟机磁盘文件(VMDK)在某个点及时的副本.可以通过使用恢复到快照来保持磁盘文件和系统存储. 1.拍摄快照 拍摄快照前先关机,然后右键点击虚拟机=> ...
- 通过一道简单的例题了解Linux内核PWN
写在前面 这篇文章目的在于简单介绍内核PWN题,揭开内核的神秘面纱.背后的知识点包含Linux驱动和内核源码,学习路线非常陡峭.也就是说,会一道Linux内核PWN需要非常多的铺垫知识,如果要学习可以 ...
- css实现hover显示下拉菜单
原理比较简单,首先先隐藏下拉菜单即display:none,当鼠标hover后,设置display:block. <style> .menu-title { postion: relati ...
- github源码下载总结
总结 下面来自我的经验,仅作参考. 下载时间选择 千万不要选择 晚上下载.下午7点后就不要从github上传或者下载代码,我用的是电信,踩坑: 这段时间后到第二天早上7点之前,这段时间内的上传和下载只 ...
- c++设计模式概述之适配器
类写的不规范(应该屏蔽类的拷贝构造函数和运算符=).少写点代码,缩短篇幅,重在理解. 实际中可不要这样做. 类比生活中的手机,pad等电源适配器. 简单来讲: 将原本 不匹配 的两者 变的匹配 ...
- 【LeetCode】172. Factorial Trailing Zeroes 解题报告(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 递归 循环 日期 题目描述 Given an integer ...
- 【LeetCode】988. Smallest String Starting From Leaf 解题报告(C++ & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS BFS 日期 题目地址:https://le ...
- Color Models (RGB, CMY, HSI)
目录 概 定义 RGB CMY CMYK HSI 相互的转换 RGB <=> CMY CMY <=> CMYK CMY > CMYK CMYK > CMY RGB ...
- pytest之 fixture 实现机制
一.相同测试数据存放优化 在讲 fixture 实现机制之前,插入一段内容 上次有个小伙伴问我说,类似下面的用例代码情况,每条测试用例的数据都一样的,我们可以怎么进行优化吗? 当然是可以的 其实我们可 ...