Promise 重写 (第一部分)

学习关键语句:
promise 重写

写在前面

重新学习了怎么重写 promise , 我觉得最重要的就是要有思路,不然有些 A+ 规范是完全想不到的

开始

重写函数的过程中, 最重要的是有思路
我们从哪里获取重写思路? 从正常的代码中
我们先看正常的代码

const p1 = new Promise((resolve, reject) => {resolve();console.log("1");
}).then(() => {console.log("2");
});

我们的思路就来了:

  1. promise 是一个类
  2. 初始化的时候传入的是一个函数, 函数里面有两个参数, resolve, reject
  3. 这说明类的构造器中有这两个函数声明
  4. 实例化后的每一个 promise 都有自己的状态, 说明有实例属性 state

想到这里马上开始重写, 完成以上 1\2\3\4 点

// 1. promise 是一个类
class MyPromise {constructor(executor) {// 4. 实例化后的每一个 promise 都有自己的状态, 说明有实例属性 statethis.state = "pending";// 3. 这说明类的构造器中有这两个函数声明let resolve = () => {};let reject = () => {};// 2. 初始化的时候传入的是一个函数, 函数里面有两个参数, resolve, rejectexecutor(resolve, reject);}
}
let p = new MyPromise((resolve, reject) => {});

接下来我们知道 resolve 和 reject 函数中有参数 value 和 reason
所以我们马上就又有思路了:

  1. 执行器函数中调用 resolve 会传入参数, reject 同样也是
  2. 需要一个 then 方法来链式调用, 所以 then 方法是一个原型方法
  3. then 方法中有两个参数, 一个成功回调一个失败回调

完成以上 5\6\7 点

class MyPromise {constructor(executor) {this.state = "pending";// 5. 执行器函数中调用 resolve 会传入参数, reject 同样也是let resolve = (value) => {};let reject = (reason) => {};executor(resolve, reject);}// 6. 需要一个 then 方法来链式调用, 所以 then 方法是一个原型方法// 7. then 方法中有两个参数, 一个成功回调一个失败回调then(onFulfilled, onRejected) {}
}
let p = new MyPromise((resolve, reject) => {});

我们知道只有在构造器函数中调用了 resolve 或者 reject 方法后才会进入 then 方法, 同时我们也知道一旦调用 resolve 或者 reject 方法就会改变 promise 实例的状态, 所以我们接下来的思路就是:

  1. 调用 resolve 或者 reject 方法后改变状态
  2. 成功或者失败回调需要拿到对应的 resolve 和 reject 函数中的参数, 这需要我们在实例上新增相应的属性存储
  3. 在 resolve 和 reject 方法中要赋值给实例属性
  4. 在 then 方法中判断状态调用方法

完成以上 8\9\10\11 点

class MyPromise {constructor(executor) {this.state = "pending";// 9. 成功或者失败回调需要拿到对应的 resolve 和 reject 函数中的参数, 这需要我们在实例上新增相应的属性存储this.value = undefined;this.reason = undefined;let resolve = (value) => {// 8. 调用 resolve 或者 reject 方法后改变状态this.state = "fulfilled";// 10. 在 resolve 和 reject 方法中要赋值给实例属性this.value = value;};let reject = (reason) => {// 8. 调用 resolve 或者 reject 方法后改变状态this.state = "rejected";// 10. 在 resolve 和 reject 方法中要赋值给实例属性this.reason = reason;};executor(resolve, reject);}then(onFulfilled, onRejected) {// 11. 在 then 方法中判断状态调用方法if (this.state === "fulfilled") {onFulfilled(this.value);}if (this.state === "rejected") {onRejected(this.reason);}}
}
let p = new MyPromise((resolve, reject) => {});

好完成到这里的时候, 不涉及异步的操作我们已经完成了, 现在我们尝试使用一下我们重写的 promise

const p1 = new MyPromise2((resolve, reject) => {resolve(2);console.log("1");
}).then((value) => {console.log(value);
});

在这里插入图片描述

接下来考虑异步的问题, 即如果执行器函数中存在异步的话, 进入 then 方法时状态其实还是 pending , 所以需要考虑当进入 then 方法时状态为 pending 就是异步, 我们要将异步时候的回调函数保存起来当真正执行的时候再调用 , 由于同一个 promise 可以多次调用 then 方法 , 所以需要用一个数组来保存所有可能的回调函数 , 我们从这出发 , 给出以下思路:

  1. 在实例上添加属性来保存成功回调和失败回调
  2. 在 then 方法中遇到 pending 状态就是有异步情况 , 真正的执行回调将不在这里发生 , 存到实例的属性上

完成以上 12\13 点

class MyPromise {constructor(executor) {this.state = "pending";this.value = undefined;this.reason = undefined;// 12. 在实例上添加属性来保存成功回调和失败回调this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];let resolve = (value) => {this.state = "fulfilled";this.value = value;};let reject = (reason) => {this.state = "rejected";this.reason = reason;};executor(resolve, reject);}then(onFulfilled, onRejected) {if (this.state === "fulfilled") {onFulfilled(this.value);}if (this.state === "rejected") {onRejected(this.reason);}// 13. 在 then 方法中遇到 pending 状态就是有异步情况, 真正的执行回调将不在这里发生, 存到实例的属性上if (this.state === "pending") {this.onFulfilledCallbacks.push(() => {onFulfilled(this.value);});this.onRejectedCallbacks.push(() => {onRejected(this.reason);});}}
}
let p = new MyPromise((resolve, reject) => {});

那么真正的回调函数执行到底在哪里呢? 我们来看一下一个出现了异步的情况代码, 看以下代码:

由于构造器函数中出现一个定时器, 所以会在 1 秒后才打印出 2

const p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve(2);}, 1000);console.log("1");
}).then((value) => {console.log(value);
});

上面这个代码中能执行回调函数的地方只有在 resolve 函数中了, 所以我们就又有思路了:

  1. 在 resolve 和 reject 方法中执行回调函数

完成以上 14 点

class MyPromise {constructor(executor) {this.state = "pending";this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];let resolve = (value) => {this.state = "fulfilled";this.value = value;// 14. 在 resolve 和 reject 方法中执行回调函数this.onFulfilledCallbacks.forEach((fn) => fn());};let reject = (reason) => {this.state = "rejected";this.reason = reason;// 14. 在 resolve 和 reject 方法中执行回调函数this.onRejectedCallbacks.forEach((fn) => fn());};executor(resolve, reject);}then(onFulfilled, onRejected) {if (this.state === "fulfilled") {onFulfilled(this.value);}if (this.state === "rejected") {onRejected(this.reason);}if (this.state === "pending") {this.onFulfilledCallbacks.push(() => {onFulfilled(this.value);});this.onRejectedCallbacks.push(() => {onRejected(this.reason);});}}
}
let p = new MyPromise((resolve, reject) => {});

我们知道 promise 实例是可以链式调用的, 这意味每一个方法的最后都会返回一个新的 promise 来提供给下一次调用 promise 原型方法, 所以我们就考虑到在 then 方法中直接返回一个新的 promise 对象, 同时, 由于执行器函数执行是同步代码, 所以我们可以直接将 then 中的代码放入到新的 promise 中, 但是我们知道链式调用 then 方法时我们可以拿到上一次 then 返回的值, 所以在成功回调返回的值, 我们将再次使用新的 promise 中的 resolve 方法去调用, 以下就是这次的思路:

  1. 在 then 方法中新声明一个 promise 对象并返回, 将 then 方法中代码放入执行器函数中
  2. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 和 reject 方法

完成以上 15\16 点

class MyPromise {constructor(executor) {this.state = "pending";this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];let resolve = (value) => {this.state = "fulfilled";this.value = value;this.onFulfilledCallbacks.forEach((fn) => fn());};let reject = (reason) => {this.state = "rejected";this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn());};executor(resolve, reject);}then(onFulfilled, onRejected) {// 15. 在 then 方法中新声明一个 promise 对象并返回, 将 then 方法中代码放入执行器函数中const p2 = new MyPromise((resolve, reject) => {let x;if (this.state === "fulfilled") {// 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法x = onFulfilled(this.value);resolve(x);}if (this.state === "rejected") {// 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法x = onRejected(this.reason);reject(x);}if (this.state === "pending") {this.onFulfilledCallbacks.push(() => {// 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法x = onFulfilled(this.value);resolve(x);});this.onRejectedCallbacks.push(() => {// 16. 将第一次 then 的回调的结果赋值给一个变量, 使用新声明的 promise 来调用 resolve 方法x = onRejected(this.reason);reject(x);});}});return p2;}
}
let p = new MyPromise((resolve, reject) => {});

好的这样就已经完成了链式调用的问题了, 但是现在又有一个新的问题了, 那就是如果第一次的回调函数返回的是一个 promise 对象呢? 那就需要对 x 进行判断和处理, 我们思路如下:

  1. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
  2. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行
  3. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹
  4. 同样的, 在构造器中, 对执行器函数使用 try.catch 包裹

完成以上 17\18\19\20 点

class MyPromise {constructor(executor) {this.state = "pending";this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];let resolve = (value) => {this.state = "fulfilled";this.value = value;this.onFulfilledCallbacks.forEach((fn) => fn());};let reject = (reason) => {this.state = "rejected";this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn());};// 20. 同样的, 在构造器中, 对执行器函数使用 try.catch 包裹try {executor(resolve, reject);} catch (err) {reject(err);}}then(onFulfilled, onRejected) {const p2 = new MyPromise((resolve, reject) => {let x;if (this.state === "fulfilled") {// 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行setTimeout(() => {// 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹try {x = onFulfilled(this.value);// 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);}if (this.state === "rejected") {// 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行setTimeout(() => {// 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹try {x = onRejected(this.reason);// 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);}if (this.state === "pending") {this.onFulfilledCallbacks.push(() => {// 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行setTimeout(() => {// 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹try {x = onFulfilled(this.value);// 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);});this.onRejectedCallbacks.push(() => {// 18. 由于 p2 此时没有初始化完毕所以无法调用, 使用定时器包裹延后执行setTimeout(() => {// 19. 由于 resolvePromise 中可能会抛出错误, 所以使用 try.catch 包裹try {x = onRejected(this.reason);// 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);});}});return p2;}
}
// 17. 声明一个 resolvePromise 专门处理判断 x 的问题, 根据 promise A+ 规范, 返回的 promise 不能与新增的 promise 相同, 在 resolvePromise 方法中需要传入 p2 进行判断
function resolvePromise(p2, x, resolve, reject) {if (p2 === x) {return reject(new Error("Error: p2 is x"));}
}
let p = new MyPromise((resolve, reject) => {});

接下来我们要继续完善 resolvePromise 中的逻辑, 我们需要判断 x 到底是不是 promise 对象, 我们使用是否是带 then 属性的对象来判断, 是就是, 不是就不是, 当是 promise 对象后, 直接调用 x 的 then 方法, 在成功回调和失败回调中分别调用 resolvePromise 传入的 resolve 和 reject

  1. x 需要是个对象或者函数
  2. x 为普通数据类型就直接 resolve
  3. 获取 x 的 then 属性, 是函数才是 promise 对象
  4. 获取 x.then 可能会报错, 使用 try.catch 包裹

完成以上 21\22\23\24 点

class MyPromise {constructor(executor) {this.state = "pending";this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];let resolve = (value) => {this.state = "fulfilled";this.value = value;this.onFulfilledCallbacks.forEach((fn) => fn());};let reject = (reason) => {this.state = "rejected";this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn());};try {executor(resolve, reject);} catch (err) {reject(err);}}then(onFulfilled, onRejected) {const p2 = new MyPromise((resolve, reject) => {let x;if (this.state === "fulfilled") {setTimeout(() => {try {x = onFulfilled(this.value);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);}if (this.state === "rejected") {setTimeout(() => {try {x = onRejected(this.reason);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);}if (this.state === "pending") {this.onFulfilledCallbacks.push(() => {setTimeout(() => {try {x = onFulfilled(this.value);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);});this.onRejectedCallbacks.push(() => {setTimeout(() => {try {x = onRejected(this.reason);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);});}});return p2;}
}
function resolvePromise(p2, x, resolve, reject) {if (p2 === x) {return reject(new Error("Error: p2 is x"));}// 21. x 需要是个对象或者函数if ((typeof x === "object" && x !== null) || typeof x === "function") {// 24. 获取 x.then 可能会报错, 使用 try.catch 包裹try {// 23. 获取 x 的 then 属性, 是函数才是 promise 对象let then = x.then;if (typeof then === "function") {then.call(x,(y) => {resolve(y);},(r) => {reject(r);});} else {resolve(x);}} catch (err) {reject(err);}} else {// 22. x 为普通数据类型就直接 resolveresolve(x);}
}
let p = new MyPromise((resolve, reject) => {});

好, 这样看起来已经很完美了, 返回的是 promise 对象我们也能解决了, 但是紧接着又有一个问题, 要是里面的 promise 再返回一个 promise 呢? 我们就想到用递归来解决这个问题, 同时我们还要解决一个问题, 那就是一个 promise 的执行器函数中只会执行遇到的第一个 resolve 或者 reject 方法, 我们用新增变量来避免重复调用

  1. 当每次返回的都是 promise 对象时, 我们递归调用判断 x 的类型的函数
  2. 声明一个变量来防止构造器函数中重复执行 resolve 和 reject 方法
  3. 每次进入 then 方法时, 如果没有参数, 就直接返回一个返回参数的函数
class MyPromise {constructor(executor) {this.state = "pending";this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];let resolve = (value) => {this.state = "fulfilled";this.value = value;this.onFulfilledCallbacks.forEach((fn) => fn());};let reject = (reason) => {this.state = "rejected";this.reason = reason;this.onRejectedCallbacks.forEach((fn) => fn());};try {executor(resolve, reject);} catch (err) {reject(err);}}then(onFulfilled, onRejected) {// 27. 每次进入 then 方法时, 如果没有参数, 就直接返回一个返回参数的函数onFulfilled =typeof onFulfilled === "function" ? onFulfilled : (value) => value;onRejected =typeof onRejected === "function"? onRejected: (reason) => {throw reason;};const p2 = new MyPromise((resolve, reject) => {let x;if (this.state === "fulfilled") {setTimeout(() => {try {x = onFulfilled(this.value);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);}if (this.state === "rejected") {setTimeout(() => {try {x = onRejected(this.reason);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);}if (this.state === "pending") {this.onFulfilledCallbacks.push(() => {setTimeout(() => {try {x = onFulfilled(this.value);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);});this.onRejectedCallbacks.push(() => {setTimeout(() => {try {x = onRejected(this.reason);resolvePromise(p2, x, resolve, reject);} catch (err) {reject(err);}}, 0);});}});return p2;}
}
function resolvePromise(p2, x, resolve, reject) {// 26. 声明一个变量来防止构造器函数中重复执行 resolve 和 reject 方法let called = false;if (p2 === x) {return reject(new Error("Error: p2 is x"));}if ((typeof x === "object" && x !== null) || typeof x === "function") {try {let then = x.then;if (typeof then === "function") {then.call(x,(y) => {if (called) return;called = true;// 25. 当每次返回的都是 promise 对象时, 我们递归调用判断 x 的类型的函数resolvePromise(p2, y, resolve, reject);},(r) => {if (called) return;called = true;reject(r);});} else {resolve(x);}} catch (err) {if (called) return;called = true;reject(err);}} else {resolve(x);}
}
let p = new MyPromise((resolve, reject) => {});

总结

ok 这样就重写完了最基本的 promise , 之后我们再来补充上 promise 上面的方法包括 catch \ race \ all 等等

我觉得虽然挺难的 , 但是如果你有思路的话,顺着思路写下来其实是没问题的

结束

学习之路漫漫啊~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云国际站:应用实时监控服务

文章目录 一、阿里云应用实时监控服务的概念 二、阿里云应用实时监控服务的优势 三、阿里云应用实时监控服务的功能 四、写在最后 一、阿里云应用实时监控服务的概念 应用实时监控服务 (Application Real-Time Monitoring Service) 作为一款云原生可观测产品平台&#xff…

基于Rabbitmq和Redis的延迟消息实现

1 基于Rabbitmq延迟消息实现 支付时间设置为30,未支付的消息会积压在mq中,给mq带来巨大压力。我们可以利用Rabbitmq的延迟队列插件实现消息前一分钟尽快处理 1.1定义延迟消息实体 由于我们要多次发送延迟消息,因此需要先定义一个记录消息…

应用协议安全:Rsync-common 未授权访问.

应用协议安全:Rsync-common 未授权访问. Rsync 是 Linux 下一款数据备份工具,支持通过 rsync 协议、ssh 协议进行远程文件传输。其中 rsync 协议默认监听 873 端口,如果目标开启了 rsync 服务,并且没有配置 ACL 或访问密码&#…

delete 与 truncate 命令的区别

直接去看原文 原文链接:【SQL】delete 与 truncate 命令的区别_truncate和delete的区别-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 1. 相同点 二者都能删除表中的数据…

uniapp插件开发

安装android studio:安装目录下bin下的此文件,是用来修改分配给android studio的占用内存。 Android 11足够用。 创建新项目: 目录结构介绍: UI组件介绍:在设计程序界面时可以使用可视化拖拽的方式,没有必要…

【Apifox】国产测试工具雄起

在开发过程中,我们总是避免不了进行接口的测试, 而相比手动敲测试代码,使用测试工具进行测试更为便捷,高效 今天发现了一个非常好用的接口测试工具Apifox 相比于Postman,他还拥有一个非常nb的功能, 在接…

Maven 的 spring-boot-maven-plugin 红色报错

1、想要处理此情况&#xff0c;在工具下面加上指定的版本号。 2、给自己的maven的setting文件加工一下。 <mirrors><!--阿里云镜像1--><mirror><id>aliyunId</id><mirrorOf>central</mirrorOf><name>aliyun maven</name>…

数据存储和内存对齐

校内课复习笔记 非数值数据表示 在计算机中&#xff0c;只有01序列&#xff0c;这串01序列是什么意思&#xff0c;由人为定义。 西文字符 在ASCII码中&#xff0c;通过一个65的偏移量&#xff0c;使得一部分无符号数指向A-Za-z。 在C语言中&#xff0c;通过char类型的转换规范…

算法萌新闯力扣:同构字符串

力扣题&#xff1a;同构字符串 开篇 对于字符串相关的题目&#xff0c;哈希表经常会使用到&#xff0c;这道题更是如此&#xff0c;还用到了两个哈希表。拿下它&#xff0c;你对字符串题目的理解就会更上一层楼。 题目链接:205.同构字符串 题目描述 代码思路 看完题目后&a…

C语言--五子棋项目【图文详解 经典】

今天小编带领大家学一学C语言入门必写的五子棋项目&#xff0c;题目非常经典&#xff0c;值得一学。 目录 一.目标效果 二.五子棋的元素 1.棋子 2.棋盘 三,需要准备的工具 四.具体内容 1.加载背景图片 2.画横线与竖线 3. 画小黑点 4.获取鼠标消息 5.画棋子 6.如何判断比…

【ERROR】ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND No package.json

1、报错 启动项目的时候&#xff0c;报这个错误&#xff0c;是因为根目录错误&#xff0c;查看&#xff0c;根目录是否错误。

Bobo Python 学习笔记

安装 Bobo 可以通过通常的方式安装&#xff0c;包括使用setup.py install 命令。当然&#xff0c;您可以使用Easy Install、Buildout或pip。 安装bobo Collecting boboDownloading bobo-2.4.0.tar.gz (17 kB) Collecting WebObDownloading WebOb-1.8.7-py2.py3-none-any.whl…

Python爬取股票交易数据代码示例及可视化展示。

文章目录 前言一、开发环境二、第三方模块三、爬虫案例步骤四、爬虫程序全部代码1.分析网页2.导入模块3.请求数据4.解析数据5.翻页6.保存数据 五、实现效果六、数据可视化全部代码1.导入数据2.读取数据3.可视化图表4.效果展示关于Python技术储备一、Python所有方向的学习路线二…

创建一个前后端分离项目:Vue+SpringBoot

这是一个基于SpringBootVue3的前后端分离的项目&#xff0c;麻雀虽小&#xff0c;五脏俱全&#xff0c;开箱即用&#xff01; 这先粗略描述一下它的前后端。JNPF开发平台的前端采用的是Vue.js&#xff0c;这是一种流行的前端JavaScript框架&#xff0c;用于构建用户界面。 后端…

第三方软件测试服务有哪些形式?选择时如何避雷?

高新技术的快速发展&#xff0c;人们对于软件产品越来越依赖&#xff0c;因此软件质量对于软件企业来说至关重要。产品质量的好坏需要通过检测才得知&#xff0c;软件企业为了获得更客观公正的检验结果&#xff0c;会将软件测试交由第三方软件测试服务机构进行?那么有哪些形式…

【milkv】0、duo编译环境搭建

一、开发资料整理 Docker https://hub.docker.com/repository/docker/dreamcmi/cv1800-docker/general GitHub https://github.com/milkv-duo/duo-buildroot-sdk CV181x/CV180x MMF SDK 开发文档汇总 https://developer.sophgo.com/thread/471.html cv181x芯片使用的交叉…

盘点72个ASP.NET Core源码Net爱好者不容错过

盘点72个ASP.NET Core源码Net爱好者不容错过 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 链接&#xff1a;https://pan.baidu.com/s/1nlQLLly_TqGrs5O8eOmZjA?pwd8888 提取码&#xff1a;8888 项目名称 (Chinese) 物业收费…

【git】解决git报错:ssh:connect to host github.com port 22: Connection timed out 亲测有效

如题&#xff0c;git使用中突然报错 ssh:connect to host github.com port 22: Connection timed out 通过查阅各种资料&#xff0c;得知原因可能是由于电脑的防火墙或者其他网络原因导致ssh连接方式 端口22被封锁。 解决方法 一&#xff1a;抛弃ssh连接方式&#xff0c;使…

图解系列--密码

1.概念 _1.对称密码与公钥密码 对称密码是指在加密和解密时使用同一密钥的方式。 公钥密码则是指在加密和解密时使用不同密钥的方式。因此&#xff0c;公钥密码又称为非对称密码。 _2.混合密码系统 对称密码和公钥密码结合起来的密码方式 _3.散列值 散列值就是用单向散列函数计…