记录--通过手写,分析Promise核心原理
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
1. 定义整体结构
- 先写出构造函数,将Promise向外暴露
/*
自定义Promise函数模块:IIFE
*/ (function (window) {
/*
Promise构造函数
executor:执行器函数
*/
function Promise(executor) { } // 向外暴露Promise
window.Promise = Promise
})()
- 添加Promise原型对象上的方法
/*
Promise原型对象的then
指定一个成功/失败的回调函数
返回一个新的promise对象
*/
Promise.prototype.then = function(onResolved,onRejected){ } /*
Promise原型对象的.catch
指定一个失败的回调函数
返回一个新的promise对象
*/
Promise.prototype.catch = function(onRejected){ }
- 添加Promise函数对象上的方法
/*
Promise函数对象的resovle方法
返回一个指定结果的promise对象
*/
Promise.resolve = function(value){ } /*
Promise函数对象的reject方法
返回一个指定reason的失败状态的promise对象
*/
Promise.reject = function(value){ } /*
Promise函数对象的all方法
返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
*/
Promise.all = function(0value){ } /*
Promise函数对象的race方法
返回一个promise对象,状态由第一个完成的promise决定
*/
Promise.race = function(value){ }
通过上面的注释可以知道。不管是Promise原型对象上的方法还是Promise函数对象上的方法 ,它们的执行结果都将返回一个Promise对象
2. 实现Promise构造函数
我们看看我们是怎么使用Promise的
const promiseA = new Promise( (resolve,reject) => {
resolve(777);
});
- 我们传入了一个函数,而且这个函数被立即执行,不仅如此,这个函数还会立即执行resolve和reject。说明构造函数里有resolve和reject方法。因此我们可以初步实现:
/*
Promise构造函数
executor:执行器函数
*/
function Promise(executor) { function resovle() { }
function reject() { } // 立即同步执行executor
executor(resovle,reject)
}
- 每个promise都有一个状态可能为pending或resolved,rejected。而且初始状态都为pending。因此需要添加个status来表示当前promise的状态.。并且每个promise有自己的data。
function Promise(executor) { var self = self
新增代码
self.status = 'pending' // 给promise对象指定status属性,初始值为pending self.data = undefined // 给promise对象指定一个存储结果的data function resovle() { }
function reject() { } // 立即同步执行executor
executor(resovle,reject)
}
此外,当我们这样使用Promise的时候,
// 例1
var promise = new Promise((resovle,reject)=>{ }) promise.then(resolve=>{},reject=>{})
这时执行到then,因为我们传入的立即执行函数没有执行resolve或者reject,所以promise的状态还是pending,这时要把then里面的回调函数保存起来,所以需要个callbacks数组
function Promise(executor) { var self = self self.status = 'pending' // 给promise对象指定status属性,初始值为pending
self.data = undefined // 给promise对象指定一个存储结果的data
新增代码
self.callbacks = [] // 每个元素的结构:{onResolved(){},onRejected(){}} function resovle() { }
function reject() { } // 立即同步执行executor
executor(resovle,reject)
}
那 then函数是怎么把传入的回调收集起来的。其实很简单,就是判断当前promise是否为pending状态,是的话,就把回调push到callbacks中。
Promise.prototype.then = function(onResolved,onRejected){ var self = this if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
}else{
} }
- 在上面的例子1的基础上,当我们执行resovle(value)时,如例2
// 例2
var promise = new Promise((resolve,reject)=>{
setTimeout(function () {
resolve(1)
})
}) promise.then(
value=>{console.log(value)},
err=>{console.log(err)}
)
此时代码执行情况是怎么样的呢?
- 先执行new Promise里的代码,然后发现个定时器,js线程将定时器交给定时器线程处理,2. 然后继续执行下面的代码,发现是then,而且当前的promise还是pending的状态。就把then里的回调函数放到callbacks中。
- 5秒后定时器线程将定时器里的回调函数(也就是宏任务)放到消息队列中,js线程在消息队列里发现了这个宏任务,就把它拿来执行。
- 执行这个宏任务,就执行了resolve(1),此时promise的callbacks里的回调被执行。并将当前promise状态改为resolved。然后这个1也会被保存到当前promise对象中
那怎么实现resolve呢?依旧上面的描述,就知道resovle的功能是执行callbacks里的函数,并保存data,并将当前promise状态改为resolved。所以我们可以这么实现
function resolve(value) {
// 将状态改为resolved
self.status = 'resolved'
// 保存value的值
self.data = value // 如果有待执行的callback函数,立即异步执行回调函数onResolved
if (self.callbacks.length>0){
self.callbacks.forEach(callbackObj=>{
callbackObj.onResolved(value)
})
}
}
- 我们还知道,promise的状态只能改变一次,因此当执行resolve的时候要判断是不是promise是不是pending的状态,否则是不能执行的
function resolve(value) {
// 如果当前状态不是pending,则不执行
if(this.status !== 'pending'){
return
}
// 将状态改为resolved
this.status = 'resolved'
// 保存value的值
this.data = value // 如果有待执行的callback函数,立即异步执行回调函数onResolved
if (this.callbacks.length>0){
setTimeout(()=>{
this.callbacks.forEach(callbackObj=>{ A
callbackObj.onResolved(value)
})
})
}
}
- 异曲同工之妙的是reject方法也是这个道理,因此这里无需赘述
function reject(value) {
// 如果当前状态不是pending,则不执行
if(self.status !== 'pending'){
return
}
// 将状态改为rejected
self.status = 'rejected'
// 保存value的值
self.data = value // 如果有待执行的callback函数,立即异步执行回调函数onResolved
if (self.callbacks.length>0){
self.callbacks.forEach(callbackObj=>{
callbackObj.onRejected(value)
})
}
}
- 我们又知道,当在执行executor的时候,如果执行异常的话,这个promise的状态会直接执行reject方法。例如
// 例 3
var promise = new Promise((resolve,reject)=>{ error;执行到这里出错了 setTimeout(function () {
resolve(1)
})
})
要实现这个功能,我们可以在executor外让try catch来捕获
try{
// 立即同步执行executor
executor(resolve,reject)
}catch (e) { // 如果执行器抛出异常,promise对象变为rejected状态
reject(e)
}
好了,现在测试下
// 例4
let promise = new Promise((resolve,reject)=>{ setTimeout(function () {
resolve(1)
//reject(1)
},100)
}) promise.then(
value=>{
console.log("onResolved:",value);
},
reason=>{
console.log("onRejected:",reason);
}
)
发现成功。 成功输出onResolved:1
3. 实现then方法
我们在上面简单的实现了当前promise为pending状态的情况,如:
Promise.prototype.then = function(onResolved,onRejected){ var self = this if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
}else{
} }
那其他情况呢?
执行到then时,promise可能会是pending状态,此时就要把then里的回调函数保存起来,也可能会是resolved或者rejected状态,此时就不用把回调保存起来,直接执行onResolved或onRejected方法。注意是异步执行。而且是做为微任务的,这里我们简单的用setTimeout来实现就好了。
Promise.prototype.then = function(onResolved,onRejected){ var self = this if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
onResolved(self.data)
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
} }
而且我们知道,执行完then是要返回一个新的promise的,而新的promise的状态则由当前then的执行结果来确定。
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{
if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
onResolved(self.data)
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
}
}) }
当 当前的promise状态为resolved的时候,则执行then的时候,会执行第二个判断语句
则当前执行第二个判断语句的时候会出现三种情况
- 如果then里的回调函数返回的不是promise,return的新promise的状态是则是resolved,value就是返回的值。例如:
// 例5
let promise = new Promise((resolve,reject)=>{
resolve(1)
}) promise.then(
value=>{
return value //返回的不是promise,是value
}
)
因此,我们可以这样实现
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{
if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
修改代码
setTimeout(()=>{
const result = onResolved(self.data)
if (result instanceof Promise){ } else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
}
}) }
简单解释下:
执行onResolved(self.data),其实就是执行例子中的下面这个回调函数
value=>{
return value //返回的不是promise,是value
}
那么这个回调函数返回了value。就把value传入resolve函数,resolve函数将当前新的promise的状态改为resolved,同时将value保存到当前新的promise的data中。
- 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果,如代码所示,我们返回一个新的promise。如果这个promise执行了resolve,返回的新的promise的状态则是resolved的。否则为rejected
// 例6
let promise = new Promise((resolve,reject)=>{
resolve(1)
}) promise.then(
value=>{
return new Promise((resolve,reject)=>{
resolve(2)
//或者
//reject(error)
})
}
)
因此我们可以这样实现
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{
if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
const result = onResolved(self.data)
if (result instanceof Promise){
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
}
}) }
在这里说明一下:
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
由于我们在例6中执行了then里的
value=>{
return new Promise((resolve,reject)=>{
resolve(2)
//或者
//reject(error)
})
}
则返回一个promise对象,这个promise对象可能为resolved状态(执行 resolve(2))也可能为rejected状态(执行reject(error))。
将会导致value => {resolve(value)},
这个回调函数的执行或者 reason => {reject(reason)}
的执行。
因此会把即将返回的新的promise的data设置为value或者,reason。会把状态设置为resolved或者rejected。
- 如果执行这段代码的时候抛出错误,则返回的promise的状态为rejected,我们可以用try catch来实现
setTimeout(()=>{
try{
const result = onResolved(self.data)
if (result instanceof Promise){
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
}catch (e) {
// 3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
})
异曲同工之妙的是当status === 'rejected',道理一样
setTimeout(()=>{
try{
const result = onRejected(self.data)
if (result instanceof Promise){
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
}catch (e) {
// 3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
})
到这里,我们发现当执行resolve的时候,onResolved(self.data)
和onRejected(self.data)
执行时也会跟上面一样的结果,可以说执行回调函数都要做以上判断,因此我们要将
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
改成
if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){
try{
const result = onResolved(self.data)
if (result instanceof Promise){
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
}catch (e) {
// 3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
},
到此,我们发现,相同的代码太多了,因此有必要封装一下
function handle(callback) {
try{
const result = callback(self.data)
if (result instanceof Promise){
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
}catch (e) {
// 3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
}
这样以来就清爽了很多
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{
/*
调用指定回调函数的处理,根据执行结果。改变return的promise状态
*/
function handle(callback) {
try{
const result = callback(self.data)
if (result instanceof Promise){
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
}catch (e) {
// 3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
}
if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){
handle(onResolved)
},
onRejected(){
handle(onRejected)
}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
handle(onResolved)
})
}else{ // 当status === 'rejected'
setTimeout(()=>{
handle(onRejected)
})
}
}) }
另外,我们还知道,promise会发生值传透,例如
let promsie = new Promise((resolve,reject)=>{
resolve(1)
})
promsie
.then(2)
.then(3)
.then(value =>console.log(value))
运行结果: 1
解释:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。值传透可以理解为,当传入then的不是函数的时候,这个then是无效的。而实际原理上其实是当then中传入的不算函数,则这个then返回的promise的data,将会保存上一个的promise.data。这就是发生值穿透的原因。而且每一个无效的then所返回的promise的状态都为resolved。
因此,要实现直传透这个特性,我们可以这样实现
添加这两句来判断要不要发生值传透
onResolved = typeof onResolved === 'function'? onResolved: value => value
onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
实际上就是改写,如果传入的不是函数,那就忽略那个传入值,自己再写一个函数。这个函数的执行结果将返回上一个promise的data
Promise.prototype.then = function(onResolved,onRejected){
onResolved = typeof onResolved === 'function'? onResolved: value => value
onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
var self = this return new Promise((resolve,reject)=>{ /*
调用指定回调函数的处理,根据执行结果。改变return的promise状态
*/
function handle(callback) {
try{
const result = callback(self.data)
if (result instanceof Promise){
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
}catch (e) {
// 3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
}
if(self.status === 'pending'){
// promise当前状态还是pending状态,将回调函数保存起来
self.callbacks.push({
onResolved(){
handle(onResolved)
},
onRejected(){
handle(onRejected)
}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
handle(onResolved)
})
}else{ // 当status === 'rejected'
setTimeout(()=>{
handle(onRejected)
})
}
}) }
3.实现catch方法
catch方法的作用跟then里的第二歌回调函数一样,因此我们可以这样来实现
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected)
}
我的天啊,居然这么简单
4. 实现Promise.resolve
我们都知道,Promise.resolve方法可以传三种值
- 不是promise
- 成功状态的promise
- 失败状态的promise
Promise.resolve(1)
Promise.resolve(Promise.resolve(1))
Promise.resolve(Promise.reject(1))
实际上跟实现上面的then时有点像
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
if (value instanceof Promise){
// 如果value 是promise
value.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else{
// 如果value不是promise
resolve(value)
} } }
5.实现Promise.reject
实现这个比较简单,返回一个状态为rejected的promise就好了
/*
Promise函数对象的reject方法
返回一个指定reason的失败状态的promise对象
*/
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
6.实现Promise.all
我们知道,这个方法会返回一个promise
/*
Promise函数对象的all方法
返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
*/
Promise.all = function(promises){
return new Promise((resolve,reject)=>{ })
}
而这个promise的状态由遍历每个promise产生的结果决定
/*
Promise函数对象的all方法
返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
*/
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
// 遍历promises,获取每个promise的结果
promises.forEach((p,index)=>{ })
})
}
有两种结果:
- 遍历到有一个promise是reject状态,则直接返回的promise状态为rejected
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
// 遍历promises,获取每个promise的结果
promises.forEach((p,index)=>{
p.then(
value => { },
reason => { //只要有一个失败,return的promise状态就为reject
reject(reason)
}
)
})
})
}
- 遍历所有的promise的状态都为resolved,则返回的promise状态为resolved,并且还要每个promise产生的值传递下去
Promise.all = function(promises){
const values = new Array(promises.length)
var resolvedCount = 0 //计状态为resolved的promise的数量
return new Promise((resolve,reject)=>{
// 遍历promises,获取每个promise的结果
promises.forEach((p,index)=>{
p.then(
value => {
// p状态为resolved,将值保存起来
values[index] = value
resolvedCount++;
// 如果全部p都为resolved状态,return的promise状态为resolved
if(resolvedCount === promises.length){
resolve(values)
}
},
reason => { //只要有一个失败,return的promise状态就为reject
reject(reason)
}
)
})
})
}
好像可以了,当其实这里还有一个问题,就是all传进去的数组不一定都是promise对象,可能是这样的
all([p,2,3,p])
因此需要把不是promise的数字包装成promise
Promise.all = function(promises){
const values = new Array(promises.length)
var resolvedCount = 0 //计状态为resolved的promise的数量
return new Promise((resolve,reject)=>{
// 遍历promises,获取每个promise的结果
promises.forEach((p,index)=>{
Promise.resolve(p).then(
value => {
// p状态为resolved,将值保存起来
values[index] = value
resolvedCount++;
// 如果全部p都为resolved状态,return的promise状态为resolved
if(resolvedCount === promises.length){
resolve(values)
}
},
reason => { //只要有一个失败,return的promise状态就为reject
reject(reason)
}
)
})
})
}
7.实现Promise.race
这个方法的实现要比all简单很多
/*
Promise函数对象的race方法
返回一个promise对象,状态由第一个完成的promise决定
*/
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
// 遍历promises,获取每个promise的结果
promises.forEach((p,index)=>{
Promise.resolve(p).then(
value => {
// 只要有一个成功,返回的promise的状态九尾resolved
resolve(value) },
reason => { //只要有一个失败,return的promise状态就为reject
reject(reason)
}
)
})
})
}
本文转载于:
https://juejin.cn/post/6856213486633304078
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--通过手写,分析Promise核心原理的更多相关文章
- 【面试题】手写async await核心原理,再也不怕面试官问我async await原理
前言 async await 语法是 ES7出现的,是基于ES6的 promise和generator实现的 generator函数 在之前我专门讲个generator的使用与原理实现,大家没了解过的 ...
- Promise核心原理解析
作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要 ...
- 通过源码分析RocketMQ主从复制原理
作者:京东物流 宫丙来 一.主从复制概述 RocketMQ Broker的主从复制主要包括两部分内容:CommitLog的消息复制和Broker元数据的复制. CommitLog的消息复制是发生在消息 ...
- 30个类手写Spring核心原理之环境准备(1)
本文节选自<Spring 5核心原理> 1 IDEA集成Lombok插件 1.1 安装插件 IntelliJ IDEA是一款非常优秀的集成开发工具,功能强大,而且插件众多.Lombok是开 ...
- Java Reference核心原理分析
本文转载自Java Reference核心原理分析 导语 带着问题,看源码针对性会更强一点.印象会更深刻.并且效果也会更好.所以我先卖个关子,提两个问题(没准下次跳槽时就被问到). 我们可以用Byte ...
- Redis核心原理与实践--事务实践与源码分析
Redis支持事务机制,但Redis的事务机制与传统关系型数据库的事务机制并不相同. Redis事务的本质是一组命令的集合(命令队列).事务可以一次执行多个命令,并提供以下保证: (1)事务中的所有命 ...
- 30个类手写Spring核心原理之依赖注入功能(3)
本文节选自<Spring 5核心原理> 在之前的源码分析中我们已经了解到,依赖注入(DI)的入口是getBean()方法,前面的IoC手写部分基本流程已通.先在GPApplicationC ...
- 抽丝剥茧分析asyncio事件调度的核心原理
先来看一下一个简单的例子 例1: async def foo(): print('enter foo ...') await bar() print('exit foo ...') async def ...
- 手写webpack核心原理,再也不怕面试官问我webpack原理
手写webpack核心原理 目录 手写webpack核心原理 一.核心打包原理 1.1 打包的主要流程如下 1.2 具体细节 二.基本准备工作 三.获取模块内容 四.分析模块 五.收集依赖 六.ES6 ...
- Redis核心原理与实践--Redis启动过程源码分析
Redis服务器负责接收处理用户请求,为用户提供服务. Redis服务器的启动命令格式如下: redis-server [ configfile ] [ options ] configfile参数指 ...
随机推荐
- 《AI驱动下的开发者新生态》-2024长沙.NET技术社区活动-诚邀大家报名
回顾 2019年初,在.NET中文社区及包括苏州.广州.深圳等地区社区等大力推动.在众多企业的大力支持下,长沙地区的开发者们发起成立了长沙.NET技术社区,并组织了<2019年长沙开发者技术大会 ...
- 【OpenGL ES】绘制圆形
1 前言 [OpenGL ES]绘制三角形 中介绍了绘制三角形的方法,[OpenGL ES]绘制正方形中介绍了绘制正方形的方法,本文将介绍绘制圆形的方法. OpenGL 以点.线段.三角形为图 ...
- Neutron详解
一:简介 一.概述 1. 传统的网络管理方式很大程度上依赖于管理员手工配置和维护各种网络硬件设备:而云环境下的网络已经变得非常复杂,特别是在多租户场景里,用户随时都可能需要创建.修改和删除网络 ...
- linux中cron表达式指南
Cron是什么? 简单来讲,cron是基于Unix的系统上的一个实用程序.它使用户能够安排任务在指定的[日期/时间]定期运行.它自然是一个伟大的工具,可以自动运行大量流程,否则需要人工干预. Cron ...
- 学习go语言编程之网络编程
Socket编程 Golang语言标准库对Socket编程进行了抽象,无论使用什么协议建立什么形式的连接,都只需要调用net.Dial()即可. Dial()函数 Dial()函数的原型如下: fun ...
- python枚举之Enum模块
枚举是与多个唯一常量值绑定的一组符号(即成员).枚举中的成员可以进行身份比较,并且枚举自身也可迭代. 枚举成员名称建议使用大写字母 # 示例 from enum import Enum,unique, ...
- 04-Redis系列之-持久化(RDB,AOF)
持久化的作用 什么是持久化 redis的所有数据保存在内存中,对数据的更新将异步的保存到硬盘上 持久化的实现方式 快照:某时某刻数据的一个完整备份(mysql的Dump,redis的RDB) 写日志: ...
- 目标检测 - VOC - xml标注格式
目标检测 - VOC - xml标注格式 相对其他计算机视觉任务,目标检测算法的数据格式更为复杂.为了对数据进行统一的处理,目标检测数据一般都会做成VOC或者COCO的格式. XML标注格式 < ...
- 【八股cover#3】计网 Q&A与知识点
计网知识点Q&A 简历cover 1.TCP/IP网络模型 网络模型 TCP/IP 协议族,它是一个分层.多协议的通信体系. TCP/IP协议族是一个四层协议系统,自底而上分别是数据链 ...
- 矩池云快速安装torch-sparse、torch-geometric等包
租用机器,按自己需要的环境选择一个环境,我这里选择的是Pytorch 1.10. 租用成功后点击租用页面的 Jupyterlab 链接. Jupyterlab 里新建一个Terminal 用来安装环境 ...