搞定JavaScript异步原理,深入学习Promise

news2024/9/20 20:40:48

JS异步及Promise基础

一、从浏览器运行机制说起

来破案啦,常说的“多线程的浏览器、单线程的Javascript”到底是什么意思?单线程的Javascript是否真的需要抽身去计时、去发http请求?第一部分我们重新从浏览器运行机制开始认识一下JavaScript异步!

1.1 多进程的浏览器

浏览器是多进程的,我们每打开标签页就会产生一个进程,因此打开的标签页越多,进程就越多,对CPU的消耗就越严重,从而出现卡顿。以Chrome浏览器为例,我们可以通过浏览器右侧设置-更多工具-任务管理器查看当前浏览器进程。Chrome浏览器主要进程包括1个浏览器进程(Browser进程)、1个GPU进程、1个网络进程、多个渲染进程和多个插件进程

浏览器是多进程的

  • 浏览器主进程(Browser进程): 主要负责界面显示、用户交互、子进程管理,同时提供存储等功能
  • GPU进程: GPU 进程的使用初衷是为了实现3D CSS的效果,只是后来网页、Chrome的UI界面都选择采用 GPU进行绘制,使得GPU成为浏览器的普遍需求。终于,Chrome 在其多进程架构上也引入了 GPU 进程
  • 网络进程: 主要负责页面的网络资源加载
  • 插件进程: 主要是负责插件的运行,因为插件容易崩溃,所以需要通过单独的插件进程将其隔离,以保证插件进程崩溃不会对浏览器和页面造成影响
  • 渲染进程: 即我们通常所说的浏览器内核,其内部是多线程的,每个标签页至少有一个渲染进程,并且多个渲染进程之间互不影响,主要负责页面渲染、脚本执行、事件处理等,我们常说的JavaScript V8引擎就运行在这个进程中,也是本文的重点

1.2 多线程的渲染进程

浏览器的渲染进程是多线程的,主要由Javascript引擎线程、GUI渲染线程、事件监听线程、定时器线程、异步http请求线程等组成。

  • Javascript引擎线程: 是Javascript内核,主要负责解析并运行Javascript脚本,不管打开了多少个标签页,一个浏览器只能有一个Javascript引擎线程运行JavaScript代码,因此JavaScript是单线程的。而异步是由子线程完成的,Javascript引擎负责调度子线程

  • GUI渲染线程: 负责浏览器界面渲染,包括解析HTML、CSS、构建DOM树、RenderObject树、布局、页面绘制等。注意Javascript引擎线程与GUI渲染线程是互斥的,GUI更新会被保存在一个队列中等到Javascript引擎空闲时才会被执行。

  • 事件监听线程: 负责对事件进行处理,归属于浏览器而不是Javascript引擎,对Javascript引擎起到辅助作用,用来控制事件循环。它管理着一个事件队列(Task Queue),当JavaScript执行碰到诸如事件绑定、Ajax异步请求、setTimeOut等,会把它交给相应线程处理(如定时器线程、异步http请求线程),拿到结果后将其回调对应任务添加到事件队列的队尾,排队等待Javascript引擎的处理。

  • 定时器线程: 我们常用的**setIntervalsetTimeout所在线程**。注意,浏览器的定时计数器并不是由Javascript引擎计数的,因为JavaScript引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性,因此通过单独线程来计时并触发定时,计时完毕后,添加到事件队列中,等待Javascript引擎空闲后执行。W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms,也就是说0ms也算是4ms。

  • **异步http请求线程:**负责处理http请求。在XMLHttpRequest连接后是通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由JavaScript引擎执行。

1.3 事件循环

简单了解过浏览器运行机制过后,为什么说“多线程的浏览器、单线程的Javascript”应该已经相对明白了,现在我们就从JavaScript事件循环(Event Loop)开始,重新认识一下异步。

1.3.1 初识事件循环机制

目前我们已经明确了Javascript是单线程的,但是这样的话,如果我们需要执行一些高耗时的操作考虑会造成阻塞,为此Javascript将任务分为同步任务和异步任务两类,同步任务在主线程上运行(这里的主线程之的是JavaScript引擎线程),形成执行栈。而在主线程之外,事件监听线程管理着一个事件队列,只要异步任务执行完毕,就会检查其是否存在回调,并把回调加入到任务队列中,当Javascript完成了同步任务清空执行栈之后就空闲下来了,JavaScript就会读取任务队列中的异步任务回调,并添加到执行栈中开始执行。流程图如下:

JavaScript Event Loop

1.3.2 ES6中的事件循环机制
  • 1. 两种异步任务类型

ES6在事件循环的概念之上新增加了任务队列(Job Queue) 的新概念,它将异步任务分成宏任务和微任务两种类型。宏任务Task(macrotask) 我们姑且叫它为正常的任务,它是由宿主(浏览器/Node)发起的,追加到下一轮事件循环。而微任务Jobs(microtask) 可以说是充了SVIP,它由JavaScript自身发起,追加到本轮事件循环,意味着微任务可以插队,在本轮事件循环结束前执行,不用等到下一轮。

  • 2. 流程图

宏任务和微任务

  • 3. 常见微任务和宏任务
宏任务(macrotask)微任务(microtask)
setTimeoutPromise.[ then/catch/finally ]、async(本质也是Promise)
setIntervalObject.observe
I/O任务MutationObserver(浏览器环境)
setImmediate(nodejs)process.nextTick(nodejs)
script代码块queueMicrotask

PS: 这里把 script 代码块想象成多个中的一个,那么会执行第一个 script 代码块中的同步代码开启宏任务,并清空微任务队列,然后再去执行第二个 script 代码块中的代码,这样就可以很好的理解了。也就是说,在一个宏任务中开启执行清理全部微任务,然后开启下一个宏任务,这样也就理解了微任务是在宏任务中追加开启的这个事实了。

二、异步回调有什么问题

从第一部分,我们已经基本了解了JavaScript异步执行机制,接下来我们讲讲ES6之前最传统的异步解决方案之一——回调(callback),从回调存在的问题理解Promise的出现原因

回调是编写和处理JavaScript程序异步逻辑的最常方式,是JavaScript最基础的异步模式。MDN中这样定义回调函数:回调函数是作为参数传给另一个函数的函数,然后通过在外部函数内部调用该回调函数以完成某种操作。太绕了!换句话说,回调函数是一个函数,他会在外部函数完成执行其他动作后再最后执行。从第一部分内容来看,回调确实可以解决单线程JavaScript线程阻塞的问题,但是它也存在许许多多的问题。

2.1 回调地狱

考虑以下代码,这就是传说中的回调地狱,一层一层的往下嵌套,形似倒三角形状,因此也叫毁灭金字塔,它使得代码的逻辑变得难以理解和维护。

setTimeout(function () {
    console.log('地狱1层到了,请不要倚靠车门,注意安全')
    setTimeout(function () {
        console.log('地狱2层到了,请不要倚靠车门,注意安全')
        setTimeout(function () { 
            console.log('地狱3层到了,请不要倚靠车门,注意安全')
            // ...还有15层呢
        }, 1000)
    }, 1000)
}, 1000)

2.2 控制反转

控制反转顾名思义就是了本来想控制它,结果反过来被控制了,我们的程序考虑需要用到许多第三方工具,你把回调传给第三方工具,让它的去执行回调,那么问题来了,你并不知道你的回调函数会不会调用过早或者过晚?会不会调用过多或过多?有没有用到提供的参数?会不会吞掉出现的错误? 这就像了手里捏着一个定时炸弹,而你却把控制权交到了人家手里,有时候会给我们带来意想不到的危害。

三、认识Promise

回调函数存在诸多问题,为此ES 6推出了Promise,有效的避免了回调地狱的出现,打开了异步世界的新大门

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

3.1 先来谈谈Promise的两个特点

在理解Promise如何规避回调函数产生的问题前,我们应该先了解Promise这两个特点:

  • 对象的状态不受外界影响Promise对象代表一个异步操作,有且只有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
  • 一旦状态改变,就不会再变,任何时候都是这个状态,都是这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

3.2 Promise如何避免回调函数的诸多问题

3.2.1 调用时间问题

回调分为异步回调和同步回调,典型同步回调如forEach,异步回调如setTimeout。在回调函数中,会在调用完成时得到结果,这个时间点你无法把控,它可能是同步的,也可能是异步的,尤其是在调用一些第三方库是根本无法确定。而在Pormise中,你无需担心这个问题,因为即使Promise被立即resolved,它也无法被同步的观察到,不可能存在调用过早的问题,而作为微任务的then,会在本次宏任务结束后、下一次宏任务启动前插队运行,也就不会出现调用过晚的问题了。

3.2.2 调用次数问题

回调被正确调用的次数应该是一次。而根据Promise的两个特点我们知道,回调在决议是会且只会调用了注册的resolvereject两个回调之一,并且只接受第一次决议的结果,就成为了不可变值,这样就保证了回调调用的次数。但是若Promise永远不被决议呢?当然也有相应的解决方案,Promiset提供了竞态接口来帮助我们解决这个问题,在之后的静态方法Promise.race中会介绍。

3.2.3 参数问题

resolve只接收一个或者零个参数,当不传参时,它会决议为undefined,而如果传入多个参数,生效的只会是第一个参数,其他参数都会被忽略,如果要传入多个参数,需要传入数组或对象,使用解构来进行传递。

3.2.4 错误或异常吞并问题

try...catch在异步代码中显然无法发挥作用,但Promise提供了catch 方法供我们进行错误处理,当然也可以使用then的第二个参数处理,将错误暴露出去,但从美观和思维模式上来说并不推荐。

3.2.4 回调地狱

最烦人的是回调地狱,但是我们的Promise会通过.then的链式流完美的将异步流程以同步操作式的流程表达出来,你甚至可以像写同步代码一样写异步代码,自然也就可以规避了它

3.3 使用Promise

3.3.1 new Promise

在ES6中,我们可以提供构造函数Promise来创建Promise实例,它有两个参数,通常用resolvereject来表示,它们由JavaScript引擎提供,无需自己部署,resolve可以将promise的状态由pending转变为fulfilled**或rejected(比如传入的参数是另一个promise,在then中会举例)**并将结果作为参数传递出去,reject则可以将promise的状态由pending转变为rejected,并将错误暴露出去,基本构造方法如下:

const promise = new Promise(function(resolve, reject) {
  // 在这里干些什么,如果是同步代码会加入执行栈立即执行
    
  if (/* 操作成功 */){
    resolve(value)
  } else {
    reject(error)
  }
})

值得注意的是,resolvereject不会终止函数的执行,所以为了避免意外,一般会这样用:return resolve(...) / return reject(...)

3.3.2 Promise.prototype.then
  • 基本用法

then方法接受两个回调函数作为参数,第一个回调函数是Promise对象的状态变为fulfilled时调用,第二个回调函数是Promise对象的状态变为rejected时调用。但这两个函数都是可选的

promise.then(function(value) {
  // 成功了,做些什么
}, function(error) {
  // 失败了,捕捉到了错误
})
  • 回调函数参数类型:正常值、promise、thenable

两个回调函数都接受Promise对象传出的值作为参数,这个参数可以是正常值,也可以是Promise实例,如果是Promise实例或者thenable值则会递归的展开,等到前面的Promise实例决议后才会决议

何为thenable可以使用鸭子类型检测判断:

// 长得像只鸭子?
// 一个有then函数作为属性的对象或函数
function isThenable(p) {
  if (
    p !== null &&
    (typeof p === 'object' || typeof p === 'function') &&
    typeof p.then === 'function'
  ) {
    return true
  } else {
    return false
  }
}

举个例子:

// p2会等p1决议后才会决议
const p1 = new Promise(function (resolve, reject) {
  resolve(2)
})
const p2 = new Promise(function (resolve, reject) {
  resolve(p1)
})

p2.then(console.log) // 2

// 如果p1出错了,p2也会
const p1 = new Promise(function (resolve, reject) {
  // ...
  reject("出错了")
})
const p2 = new Promise(function (resolve, reject) {
  // ...
  resolve(p1)
})
p2.then() // Uncaught (in promise) 出错了,并且p1、p2的状态都变成了rejected!
  • 执行顺序:同步代码、异步代码和决议

promise在创建时,同步代码会立即执行,异步代码会根据宏任务还是微任务来判断执行顺序,而then会在决议后的那一轮轮循环前追加到最后运行,举个例子:

// 以下打印顺序为“1 2 3 4”
let promise = new Promise(function(resolve, reject) {
  console.log('1')
  setTimeout(console.log,0,'4')
  resolve()
})

promise.then(function() {
  console.log('3')
}) 

console.log('2')

// 以下打印顺序为“1 2 4 3”
let promise = new Promise(function(resolve, reject) {
  console.log('1')
  setTimeout(m => {
      resolve()
      console.log(m)
  },0,'4')
  
})

promise.then(function() {
  console.log('3')
}) 

console.log('2')
  • 链式调用

注意,then方法的返回值是一个新的Promise对象,这就意味着我们可以链式的调用它!这样的话我们就可以像写同步代码一样写出异步代码

链式调用

new Promise(function (resolve, reject) {
  resolve(1)
}).then(v => {
    console.log(v)
    return 2
}).then(console.log)
// 1 
// 2
Promise.prototype.catch

在Promise中,reject方法可以理解为抛出错误,而Promise.prototype.catch等价于.then(null, rejection).then(undefined, rejection),用于捕捉错误。但是一般来说,我们不会在then()方法里面使用第二个参数,而总是使用catch方法,因为更加接近同步try…catch写法,视觉上也更加直观。我们可以通过try…catch来理解它:

// 不用catch而使用try...catch
const p = new Promise(function(resolve, reject) {
  try {
    throw new Error('发生了错误');
  } catch(e) {
    reject(e)
  }
})
promise.catch(function(error) {
  console.log(error)
})

// 使用catch,更加简洁
const p = new Promise(function(resolve, reject) {
  reject(new Error('发生了错误'))
})
promise.catch(function(error) {
  console.log(error)
})

但是需要注意两点:

  • ①如果 Promise 状态已经变成fulfilled,再抛出错误是无效的
const promise = new Promise(function(resolve, reject) {
  resolve('完成了决议,结果为fulfilled')
  throw new Error('test')
})
promise
  .then(function(value) { console.log(value) }) // 完成了决议,结果为fulfilled
  .catch(function(error) { console.log(error) }) // 不会运行这里的代码
  • ②如果没有使用catch()方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应,但浏览器会打印错误
const p1 = new Promise(function (resolve, reject) {
  // 写一个错误,改变常量x
  const x = 1
  x = 2 
  // 决议
  resolve("fulfilled")
})
console.log('我还是运行了,想不到吧')
// 我还是运行了,想不到吧
// Uncaught (in promise) TypeError: Assignment to constant variable. 虽然报错,但无法阻止代码继续稳健运行
3.3.3 静态方法Promise.all(iterable)
  • 这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象状态都为fulfilled的时候才为fulfilled,一旦有任何一个iterable里面的promise对象状态为rejected,则立即让该promise对象状态转变为rejected
  • 这个新的promise对象在触发fulfilled状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致
  • 如果这个新的promise对象触发了rejected状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息
const promises = [1, 2, 3].map(function (id) {
  return request('https://juejin.cn/post/${id}');
});

Promise.all(promises).then(function (readArticles) {
  // ...
}).catch(function(handleError){
  // ...
});
3.3.4 静态方法Promise.race(iterable)

当iterable参数里的任意一个子promise被触发状态转变为fulfilledrejected后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。可以用来处理超时或者说promise永远不决议的情况,给它应该特定的时间,每决议就返回另一个promise:

// timeoutPromise辅助函数
function timeoutPromise(delay){
    return new Promise(function(resolve, reject) {
        setTimeout(function(){
			reject("一不小心就超时了")
        })
	})
}

// 利用竞态Promise.race处理超时情况
Promise.race([
    foo(),
    timeoutPromise(3000)
]).then(
  function () {
    // foo在约定时间类完成决议
  },
  function (err) {
    // foo()被拒绝或超时,查看err了解是什么情况
  }
)
3.3.5 静态方法Promise.resolve(value)

返回一个状态由给定value决定的Promise对象。注意,返回的promise状态不一定是fulfilled的!

  • 参数是一个Promise实例:

    将不做任何修改、原封不动地返回这个实例

  • 参数是一个thenable对象:

    将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法,其状态可能转变为fulfilled/rejected

  • 参数不是具有then()方法的对象,或根本就不是对象:

    返回一个新的 Promise 对象,状态为fulfilled

  • 不传递任何参数:

    直接返回一个fulfilled状态的 Promise 对象

// 通过 Promise.resolve 返回 rejected 状态的例子
let rejectThenable = {
    then: (resolve, reject) => {
        reject("reject reason")
    }
}
let rejectedPromise = Promise.resolve(rejectThenable)
3.3.6 静态方法Promise.reject(reason)

返回一个状态为rejected的Promise对象,并将给定的失败信息原封不动的传递给对应的处理方法

const promise = Promise.reject('一不小心出错了') 
// 等价于
const promise = new Promise((resolve, reject) => reject('一不小心出错了'))

promise.catch(error => {
    console.log(error) // 一不小心出错了
})
3.3.7 Promise.allSettled(iterable)

ES2020引入,该方法返回一个promise,该promise在所有promise定型后完成。并带有一个对象数组,每个对象对应每个promise的结果。返回的新的 Promise 实例,一旦发生状态变更,状态总是fulfilled。状态变成fulfilled后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。

const resolved = Promise.resolve('成功了')
const rejected = Promise.reject('失败了')
const allSettledPromise = Promise.allSettled([resolved, rejected])

allSettledPromise.then(res => {
    console.log(res)
})
// [
//    { status: 'fulfilled', value: '成功了' },
//    { status: 'rejected', reason: '失败了' }
// ]
3.3.8 Promise.any(iterable)

ES2021引入,接收一个Promise对象的集合,当其中的一个promise状态变为fulfilled时,就返回那个成功的promise,如果所有promise实例都变成rejected状态,这返回的promise实例也会变成rejected状态,并可以捕获错误AggregateError: All promises were rejected

// 成功时
const pErr = new Promise((resolve, reject) => {
  reject("总是失败")
})

const pSlow = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "最终完成")
})

const pFast = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "很快完成")
})

Promise.any([pErr, pSlow, pFast]).then((value) => {
  console.log(value) // "很快完成"
})

// 失败时
const pErr = new Promise((resolve, reject) => {
  reject('总是失败')
})

Promise.any([pErr]).catch((err) => {
  console.log(err) // "AggregateError: No Promise in Promise.any was resolved"
})
3.3.9 两个附加方法donefinally

这两个方法不在ES6中,需要自行部署,但十分有用

Promise.prototype.done

Promise 对象的回调链中,就算你在最后加入catch,如果你catch内本身出现了错误还是无法被捕捉并暴露出去,因此部署一个done方法,它总是处于回调链的尾端,保证抛出任何可能出现的错误。其实现十分简单:

Promise.prototype.done = function (onFulfilled, onRejected) {
  this
    .then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 在最后抛出一个全局错误
      setTimeout(() => {
        throw reason
      }, 0)
    })
}
Promise.prototype.finally

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。其实现也非常简单:

Promise.prototype.finally = function (callback) {
  let P = this.constructor
  return this.then(
    value => P.resolve(callback()).then(() => value),
    reason =>
      P.resolve(callback()).then(() => {
        throw reason
      })
  )
}

四、Promise性能如何

4.1 毋庸置疑的结果

把基本的基于回调的异步任务链与 Promise 链中需要移动的部分数量进行比较。很显然, Promise 进行的动作要多一些,这自然意味着它也会稍慢一些。但是与之相对的你得到了大量的内建的可信任性和更易于理解、更加直观的异步模式,规避了回调模式产生的回调地狱和控制反转问题,推荐使用。

参考书籍

《你不知道的JavaScript中卷》

《ES6标准入门》

《MDN web docs》

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

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

相关文章

828华为云征文 | Flexus X实例与华为云EulerOS的Tomcat安装指南

文章目录 前言安全组设置操作步骤软件安装配置软件验证Tomcat安装是否成功 总结 前言 Tomcat是一个由Apache软件基金会开发并维护的免费、开源的Web应用服务器。它主要用于处理Java Servlet、JavaServer Pages(JSP)和JavaServer Pages Standard Tag Lib…

新增一个数组传递给后端

实现的效果&#xff1a; 页面 <div style"margin-bottom: 10px" v-if"totalPrice"><p style"font-weight: bolder;margin-bottom: 10px">支付计划<el-button type"text" size"small" click"addPayInf…

53 mysql pid 文件的创建

前言 接上一篇文章 mysql 启动过程中常见的相关报错信息 在 mysql 中文我们在 “service mysql start”, “service mysql stop” 经常会碰到 mysql.pid 相关的错误信息 比如 “The server quit without updating PID file” 我们这里来看一下 mysql 中 mysql.pid 文件的…

软件工程知识点总结(2):需求分析(一)——用例建模

1 软件项目开发流程&#xff1a; 需求分析→概要设计→详细设计→编码实施→测试→产品提 交→维护 2 系统必须做什么&#xff1f; 获取用户需求&#xff0c;从用户角度考虑&#xff0c;用户需要系统必须完成哪些工作&#xff0c;也就是对目 标系统提出完整、准确、清晰、具体…

算法day22|组合总和 (含剪枝)、40.组合总和II、131.分割回文串

算法day22|组合总和 &#xff08;含剪枝&#xff09;、40.组合总和II、131.分割回文串 39. 组合总和 &#xff08;含剪枝&#xff09;40.组合总和II131.分割回文串 39. 组合总和 &#xff08;含剪枝&#xff09; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 ta…

使用Node-API实现跨语言交互开发流程

一、前言 使用Node-API实现跨语言交互&#xff0c;首先需要按照Node-API的机制实现模块的注册和加载等相关动作。 ArkTS/JS侧&#xff1a;实现C方法的调用。代码比较简单&#xff0c;import一个对应的so库后&#xff0c;即可调用C方法。 Native侧&#xff1a;.cpp文件&#xf…

【C语言】十六进制、二进制、字节、位、指针、数组

【C语言】十六进制、二进制、字节、位 文章目录 [TOC](文章目录) 前言一、十六进制、二进制、字节、位二、变量、指针、指针变量三、数组与指针四、指针自加运算五、二维数组与指针六、指向指针的指针七、指针变量作为函数形参八、函数指针九、函数指针数组十、参考文献总结 前…

JVM面试(六)垃圾收集器

目录 概述STW收集器的并发和并行 Serial收集器ParNew收集器Parallel Scavenge收集器Serial Old收集器Parallel Old收集器CMS收集器Garbage First&#xff08;G1&#xff09;收集器 概述 上一章我们分析了垃圾收集算法&#xff0c;那这一章我们来认识一下这些垃圾收集器是如何运…

某云彩SRM2.0任意文件下载漏洞

文章目录 免责申明搜索语法漏洞描述漏洞复现修复建议 免责申明 本文章仅供学习与交流&#xff0c;请勿用于非法用途&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任 搜索语法 fofa icon_hash"1665918155"漏洞描述 某云采 SRM2.0是一款先…

制作自己的游戏:打砖块

文章目录 &#x1f680; 前言&#x1f680; 前期准备&#x1f680; 玩法设计&#x1f680; 游戏场景&#x1f353; 什么是游戏场景&#x1f353; 绘制左上角积分&#x1f353; 绘制右上角生命值&#x1f353; 绘制砖块&#x1f353; 绘制小球&#x1f353; 绘制挡板&#x1f35…

场景是人工智能第四要素,是垂直领域人工智能的第一要素。

"场景是人工智能的第四要素&#xff0c;与数据、算力、算法同等重要。"拿着技术找场景&#xff0c;还是拿着场景找技术&#xff1f;这个锤子和钉子的问题&#xff0c;一直困扰着各家AI大厂。从近5年的实践来看&#xff0c;拿着场景找技术是更为稳健的&#xff0c;否则…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《面向电网调峰的聚合温控负荷多目标优化控制方法 》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

R901085689比例流量控制阀配置HE-SP1比例放大器

R901085689比例流量控制阀配置HE-SP1比例放大器的功能是将电信号转换成对应的流量变化&#xff0c;通过调整阀门开度来控制介质的流量。这种转换是通过比例电磁铁实现的&#xff0c;它将输入的电流信号转换成力或位移&#xff0c;从而驱动阀芯移动&#xff0c;实现流量的连续调…

html+css+js网页设计 珠宝首饰模版13个页面

htmlcssjs网页设计 珠宝首饰模版13个页面 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&…

手机同时传输USB功能与充电的实现及LDR6500的作用

在智能设备日益普及的今天&#xff0c;用户对于手机的功能需求愈发多样化&#xff0c;其中同时实现USB数据传输与充电功能成为了许多用户的迫切需求。这一功能的实现离不开先进的硬件技术和创新的芯片解决方案&#xff0c;而LDR6500正是这样一款能够满足这一需求的USB PD&#…

uni-app 扫码优化:谈谈我是如何提升安卓 App 扫码准确率的

一. 前言 之前的一个项目遭到用户吐槽&#xff1a;“你们这个 App 扫码的正确率太低了&#xff0c;尤其是安卓的设备。经常性的扫码扫不出来&#xff0c;就算是扫出来了&#xff0c;也是错误的结果&#xff01;” 由于之前是扫描二维码的需求&#xff0c;所以没有对扫描条形码…

yolov8-obb旋转目标检测onnxruntime和tensorrt推理

onnxruntime推理 导出onnx模型&#xff1a; from ultralytics import YOLO model YOLO("yolov8n-obb.pt") model.export(format"onnx") onnx模型结构如下&#xff1a; python推理代码&#xff1a; import cv2 import math import numpy as np impo…

全面提升管理效率的智慧园区可视化系统

通过图扑 HT 搭建智慧园区可视化&#xff0c;实时监测和展示园区内各设施的状态与能耗&#xff0c;优化资源配置&#xff0c;提升园区管理效率。

科普神文,一次性讲透AI大模型的核心概念

令牌&#xff0c;向量&#xff0c;嵌入&#xff0c;注意力&#xff0c;这些AI大模型名词是否一直让你感觉熟悉又陌生&#xff0c;如果答案肯定的话&#xff0c;那么朋友&#xff0c;今天这篇科普神文不容错过。我将结合大量示例及可视化的图形手段&#xff0c;为你由浅入深一次…

电脑怎么禁止软件联网?电脑怎么限制软件上网?方法很多,这三种最常用!

在日常使用电脑时&#xff0c;某些软件可能会自动联网&#xff0c;这不仅会消耗网络资源&#xff0c;还可能带来安全风险。此外企业老板考虑到公司员工可能会在工作期间访问无关软件&#xff0c;影响工作效率&#xff0c;因此&#xff0c;很多用户希望能够禁止某些软件联网&…