当前位置: 首页 > news >正文

JavaScript系列03-异步编程全解析

本文介绍了异步相关的内容,包括:

  1. 回调函数与回调地狱
  2. Promise详解
  3. async/await语法
  4. Generator函数
  5. 事件循环机制
  6. 异步编程最佳实践

1、回调函数与回调地狱

JavaScript最初是为处理网页交互而设计的语言,异步编程是其核心特性之一。最早的异步编程方式是使用回调函数。

什么是回调函数?

回调函数是作为参数传递给另一个函数的函数,并在特定事件发生后执行。


// 基本回调示例function fetchData(callback) {setTimeout(() => {const data = { name: "张三", age: 30 };callback(data);}, 1000);}fetchData(function(data) {console.log("数据获取成功:", data);});

回调函数使我们能够非阻塞地执行代码,这对于网络请求、文件操作等耗时任务尤为重要。

回调地狱问题

当多个异步操作需要依次执行时,回调函数会嵌套在一起,形成所谓的"回调地狱"(Callback Hell):


fetchUserData(function(user) {console.log("获取用户信息:", user);fetchUserPosts(user.id, function(posts) {console.log("获取用户文章:", posts);fetchPostComments(posts[0].id, function(comments) {console.log("获取文章评论:", comments);fetchCommentAuthor(comments[0].authorId, function(author) {console.log("获取评论作者:", author);// 还可以继续嵌套...});});});});

回调地狱带来的问题:

  • 代码可读性差,形成"金字塔"结构

  • 错误处理复杂

  • 变量作用域容易混淆

  • 代码维护困难

2、Promise详解

Promise是JavaScript中解决回调地狱的第一个标准方案,ES6正式将其纳入规范。

Promise的基本概念

Promise是一个代表异步操作最终完成或失败的对象。它有三种状态:

  • pending: 初始状态,既未完成也未失败

  • fulfilled: 操作成功完成

  • rejected: 操作失败

一旦Promise状态改变,就不能再变,这就是"承诺"的含义。


// 创建Promiseconst promise = new Promise((resolve, reject) => {// 异步操作setTimeout(() => {const success = Math.random() > 0.5;if (success) {resolve("操作成功"); // 成功时调用} else {reject("操作失败"); // 失败时调用}}, 1000);});// 使用Promisepromise.then(result => {console.log(result); // "操作成功"}).catch(error => {console.log(error); // "操作失败"}).finally(() => {console.log("无论成功失败都会执行");});

Promise链式调用

Promise的优势之一是支持链式调用,可以优雅地处理依赖于前一个异步操作结果的多个异步操作:


fetchUserData(userId).then(user => {console.log("用户数据:", user);return fetchUserPosts(user.id); // 返回新的Promise}).then(posts => {console.log("用户文章:", posts);return fetchPostComments(posts[0].id);}).then(comments => {console.log("文章评论:", comments);return fetchCommentAuthor(comments[0].authorId);}).then(author => {console.log("评论作者:", author);}).catch(error => {// 捕获链中任何位置的错误console.error("发生错误:", error);});

这种链式写法将原本嵌套的回调拍平,提高了代码的可读性。

Promise常用方法

Promise类提供了几个实用的静态方法:

Promise.all()

Promise.all(): 并行执行多个Promise,当所有Promise都成功时返回结果数组


// 同时发起多个请求const promises = [fetch('https://api.example.com/users'),fetch('https://api.example.com/posts'),fetch('https://api.example.com/comments')];Promise.all(promises).then(responses => Promise.all(responses.map(res => res.json()))).then(data => {const [users, posts, comments] = data;console.log(users, posts, comments);}).catch(error => {// 任一请求失败都会进入catchconsole.error("至少有一个请求失败:", error);});

Promise.race()

Promise.race(): 返回最先解决或拒绝的Promise结果


// 实现超时功能function fetchWithTimeout(url, ms) {const fetchPromise = fetch(url);const timeoutPromise = new Promise((_, reject) => {setTimeout(() => reject(new Error("请求超时")), ms);});return Promise.race([fetchPromise, timeoutPromise]);}fetchWithTimeout('https://api.example.com/data', 3000).then(response => response.json()).then(data => console.log(data)).catch(error => console.error(error));

Promise.allSettled()

Promise.allSettled(): ES2020引入,等待所有Promise完成(无论成功或失败)


Promise.allSettled([Promise.resolve(1),Promise.reject('错误'),Promise.resolve(3)]).then(results => {console.log(results);// [// { status: "fulfilled", value: 1 },// { status: "rejected", reason: "错误" },// { status: "fulfilled", value: 3 }// ]});

Promise.any()

Promise.any(): ES2021引入,返回首个成功的Promise结果


// 尝试从多个源获取数据,返回最先成功的Promise.any([fetch('https://api-1.example.com/data').then(r => r.json()),fetch('https://api-2.example.com/data').then(r => r.json()),fetch('https://api-3.example.com/data').then(r => r.json())]).then(data => console.log("获取到数据:", data)).catch(errors => console.error("所有请求均失败:", errors));

4、Generator函数

Generator函数是ES6引入的新特性,它允许函数在执行过程中暂停和恢复,这使得它特别适合实现异步控制流。

Generator基础

Generator函数在声明时使用星号(*)标记,内部使用yield关键字暂停执行:


function* numberGenerator() {yield 1;yield 2;yield 3;}const gen = numberGenerator();console.log(gen.next()); // { value: 1, done: false }console.log(gen.next()); // { value: 2, done: false }console.log(gen.next()); // { value: 3, done: false }console.log(gen.next()); // { value: undefined, done: true }

生成器返回一个迭代器,调用next()方法会执行代码直到遇到下一个yield语句。

使用Generator实现异步流程控制

Generator可以用来处理异步操作,但通常需要一个运行器函数:


function fetchData(url) {return new Promise((resolve, reject) => {setTimeout(() => {if (Math.random() > 0.3) {resolve(`来自${url}的数据`);} else {reject(`获取${url}失败`);}}, 1000);});}function* fetchSequence() {try {const user = yield fetchData('/api/user');console.log(user);const posts = yield fetchData('/api/posts');console.log(posts);const comments = yield fetchData('/api/comments');console.log(comments);return '所有数据获取完成';} catch (error) {console.error('出错了:', error);return '数据获取过程出错';}}// 手动运行生成器function runGenerator(generatorFn) {const generator = generatorFn();function handle(result) {if (result.done) return Promise.resolve(result.value);return Promise.resolve(result.value).then(res => handle(generator.next(res))).catch(err => handle(generator.throw(err)));}return handle(generator.next());}runGenerator(fetchSequence).then(result => console.log(result)).catch(err => console.error(err));

4、async/await语法

尽管Promise已经比回调函数有了很大改进,但ES2017引入的async/await语法进一步简化了异步编程,使异步代码看起来更像同步代码。

async/await基础

  • async:声明一个异步函数,它会返回一个Promise

  • await:暂停异步函数的执行,等待Promise解决

实现原理

(1)生成器与迭代器

async/await 的核心原理是利用生成器函数(Generator)的暂停和恢复能力:

function* genFunc() {yield 1;yield 2;
}

生成器可以通过 yield 暂停执行,并在之后通过 next() 恢复执行。

(2)Promise 结合

async/await 将 Generator 与 Promise 结合:

  • async 标记的函数总是返回 Promise
  • await 操作会暂停函数执行,等待 Promise 完成

(3)自动执行器

关键环节是一个自动执行器,负责:

  1. 执行生成器函数
  2. 当遇到 yield 时暂停
  3. 等待 Promise 解决
  4. 将结果传回生成器并恢复执行

简化版实现

一个简化的 async/await 实现可以是:

function asyncToGenerator(generatorFunc) {return function() {const gen = generatorFunc.apply(this, arguments);return new Promise((resolve, reject) => {function step(key, arg) {let result;try {result = gen[key](arg);} catch (error) {return reject(error);}const { value, done } = result;if (done) {return resolve(value);} else {return Promise.resolve(value).then(val => step("next", val),err => step("throw", err));}}step("next");});};
}

Babel 转译示例

以下是 Babel 如何将 async/await 转译为 ES5 代码(简化版):

// 原始 async 函数
async function foo() {const result = await someAsyncFunc();return result;
}// 转译后
function foo() {return _asyncToGenerator(function* () {const result = yield someAsyncFunc();return result;});
}

工作流程

  1. 当调用 async 函数时,自动创建一个 Promise 对象
  2. 函数体内代码正常执行,直到遇到 await 表达式
  3. await 表达式会暂停当前函数执行
  4. await 后的表达式会被转换成 Promise(如果不是已经是 Promise)
  5. 当该 Promise 完成时,恢复函数执行并返回 Promise 的结果
  6. 如果 Promise 被拒绝,await 表达式会抛出错误

// 使用async/await重写前面的例子async function getUserInfo(userId) {try {const user = await fetchUserData(userId);console.log("用户数据:", user);const posts = await fetchUserPosts(user.id);console.log("用户文章:", posts);const comments = await fetchPostComments(posts[0].id);console.log("文章评论:", comments);const author = await fetchCommentAuthor(comments[0].authorId);console.log("评论作者:", author);return author;} catch (error) {console.error("发生错误:", error);}}// 调用异步函数getUserInfo(123).then(result => console.log("最终结果:", result));

相比Promise链,async/await的优势:

  • 代码结构清晰,接近同步写法

  • 便于使用条件语句和循环

  • 易于进行错误处理

  • 调试更简单

并行执行

虽然await会暂停函数执行,但有时我们需要并行执行多个异步操作:


async function fetchAllData() {// 错误示范:串行执行,效率低const users = await fetchUsers();const posts = await fetchPosts();const comments = await fetchComments();// 正确示范:并行执行const [users, posts, comments] = await Promise.all([fetchUsers(),fetchPosts(),fetchComments()]);return { users, posts, comments };}

错误处理

async函数中可以使用try/catch来捕获错误,也能捕获await的Promise拒绝:


async function fetchWithErrorHandling() {try {const response = await fetch('https://api.example.com/data');if (!response.ok) {throw new Error(`HTTP错误: ${response.status}`);}const data = await response.json();return data;} catch (error) {console.error("获取数据失败:", error);// 可以返回默认值return { error: true, message: error.message };}}

Generator vs async/await

在ES2017引入async/await之前,Generator曾经是实现异步控制流的重要工具。现在,async/await基本上取代了Generator在异步编程中的角色,因为:

  • async/await是基于Generator和Promise的语法糖,更易于使用

  • async函数无需运行器,浏览器原生支持

  • 错误处理更加直观

然而,Generator在某些场景(如惰性计算、状态机实现)中仍然非常有用。

5、事件循环机制

要真正理解JavaScript的异步编程,必须了解底层的事件循环机制。JavaScript是单线程的,依靠事件循环来处理异步操作。

事件循环的关键组件

事件循环机制涉及以下几个关键组件:

  • 执行栈(Call Stack):管理函数调用的栈结构,遵循"后进先出"原则

  • 宏任务队列(Macrotask Queue):存放宏任务,如setTimeout、setInterval、I/O等

  • 微任务队列(Microtask Queue):存放微任务,如Promise回调、MutationObserver等

  • 事件循环(Event Loop):持续检查执行栈和任务队列的循环过程

宏任务与微任务

宏任务(Macrotask)包括:

  • script(整体代码)

  • setTimeout/setInterval

  • setImmediate(Node.js环境)

  • I/O操作

  • UI渲染(浏览器)

  • requestAnimationFrame(浏览器)

微任务(Microtask)包括:

  • Promise.then/catch/finally

  • MutationObserver

  • process.nextTick(Node.js环境)

  • queueMicrotask()

事件循环的基本流程

(1) 开始:执行第一个宏任务,即全局代码(script)

(2) 同步代码执行

  • 所有同步代码进入执行栈按顺序执行

  • 如遇异步API,其回调函数被分发到对应的任务队列中

(3) 执行栈清空

  • 同步代码执行完毕,执行栈清空

(4) 处理微任务

  • 检查微任务队列,有微任务则依次执行所有微任务

  • 执行过程中产生的新微任务也会在当前循环中执行

(5) UI渲染(仅浏览器环境):

  • 如有必要,进行页面渲染更新

(6) 处理宏任务

  • 从宏任务队列取出一个任务执行

  • 执行完后,返回步骤3,检查微任务队列

(7) 循环往复

  • 事件循环无限继续,直到所有任务队列清空

事件循环流程图:

在这里插入图片描述

实际例子解析


console.log('1. 开始'); // 同步代码setTimeout(() => {console.log('2. 第一个宏任务');Promise.resolve().then(() => {console.log('3. 宏任务中的微任务');});}, 0);Promise.resolve().then(() => {console.log('4. 第一个微任务');setTimeout(() => {console.log('5. 微任务中的宏任务');}, 0);});console.log('6. 结束'); // 同步代码// 输出顺序: 1 -> 6 -> 4 -> 2 -> 3 -> 5

(1) 第一个宏任务(script全局代码)

  • 执行同步代码,打印"1. 开始"

  • 遇到setTimeout,其回调被添加到宏任务队列

  • 遇到Promise.then,其回调被添加到微任务队列

  • 执行同步代码,打印"6. 结束"

  • 同步代码执行完毕,执行栈清空

(2) 检查微任务队列

  • 执行微任务,打印"4. 第一个微任务"

  • 遇到setTimeout,其回调被添加到宏任务队列

  • 微任务队列清空

(3) 进行UI渲染(如需)

(4) 取出下一个宏任务

  • 执行第一个setTimeout的回调,打印"2. 第一个宏任务"

  • 遇到Promise.then,其回调被添加到微任务队列

(5) 再次检查微任务队列

  • 执行微任务,打印"3. 宏任务中的微任务"

  • 微任务队列清空

(6) 进行UI渲染(如需)

(7) 取出下一个宏任务

  • 执行第二个setTimeout的回调,打印"5. 微任务中的宏任务"

关于async/await在事件循环中的位置

前面讲到async/await 就是生成器和Promise的语法糖,它的工作流程中讲到,await 表达式会暂停当前函数执行,await 后的表达式会被转换成 Promise(如果不是已经是 Promise),所以:

  • 当函数遇到 await 时,会将后续代码作为微任务放入事件循环
  • 这就是为什么 await 之后的代码总是在当前同步代码执行完毕后执行

6、异步编程最佳实践

使用Promise而非回调

所有新代码应该优先使用Promise API而非传统回调:


// 不推荐function fetchData(callback) {setTimeout(() => {callback(null, { data: 'success' });}, 1000);}// 推荐function fetchData() {return new Promise((resolve) => {setTimeout(() => {resolve({ data: 'success' });}, 1000);});}

优先使用async/await

对于大多数异步操作,使用async/await可以使代码更清晰:


// Promise链function getUserData(userId) {return fetchUser(userId).then(user => {return fetchPosts(user.id).then(posts => {user.posts = posts;return user;});});}// 使用async/awaitasync function getUserData(userId) {const user = await fetchUser(userId);user.posts = await fetchPosts(user.id);return user;}

正确处理错误

异步代码中的错误处理尤为重要:


// Promise错误处理fetchData().then(data => processData(data)).then(result => displayResult(result)).catch(error => {console.error('发生错误:', error);showErrorMessage(error);});// async/await错误处理async function handleData() {try {const data = await fetchData();const result = await processData(data);displayResult(result);} catch (error) {console.error('发生错误:', error);showErrorMessage(error);}}

避免嵌套async函数

当不需要等待内部异步操作时,避免嵌套async函数:


// 不好的实践async function processItems(items) {const results = [];for (const item of items) {// 没必要使用async函数results.push(await (async () => {const data = await fetchData(item.id);return processData(data);})());}return results;}// 更好的实践async function processItems(items) {const results = [];for (const item of items) {const data = await fetchData(item.id);results.push(processData(data));}return results;}

合理使用Promise并行执行

当多个异步操作相互独立时,应该并行执行它们:


// 低效方式:串行执行async function loadData() {const users = await fetchUsers();const products = await fetchProducts();const categories = await fetchCategories();return { users, products, categories };}// 高效方式:并行执行async function loadData() {const [users, products, categories] = await Promise.all([fetchUsers(),fetchProducts(),fetchCategories()]);return { users, products, categories };}

避免不必要的async/await

不是所有返回Promise的函数都需要async关键字:


// 不必要的asyncasync function getData() {return fetch('/api/data').then(r => r.json());}// 简化版本function getData() {return fetch('/api/data').then(r => r.json());}

使用Promise工具方法

利用Promise提供的静态方法简化常见任务:


// 并行请求并使用所有结果Promise.all([fetchUsers(), fetchPosts(), fetchComments()]).then(([users, posts, comments]) => {// 处理所有数据});// 超时处理function fetchWithTimeout(url, timeout = 5000) {return Promise.race([fetch(url),new Promise((_, reject) => {setTimeout(() => reject(new Error('请求超时')), timeout);})]);}// 任一请求成功即可function fetchFromMultipleSources(urls) {return Promise.any(urls.map(url => fetch(url)));}

编写可测试的异步代码

良好的异步代码应该易于测试:


// 可测试的异步函数async function processUserData(userId) {const user = await fetchUser(userId);if (!user) {throw new Error('用户不存在');}user.lastActive = new Date();return saveUser(user);}// 测试代码test('processUserData成功处理用户', async () => {// 使用mock替换真实APIfetchUser = jest.fn().mockResolvedValue({ id: 1, name: '张三' });saveUser = jest.fn().mockResolvedValue({ success: true });const result = await processUserData(1);expect(result.success).toBe(true);expect(saveUser).toHaveBeenCalledWith(expect.objectContaining({id: 1,lastActive: expect.any(Date)}));});

总结

JavaScript异步编程经历了从回调函数、Promise、Generator到async/await的演进。这些技术的发展使得异步代码越来越接近同步代码的直观性和可维护性,同时保留了非阻塞执行的优势。

理解事件循环机制是掌握JavaScript异步编程的关键,它解释了不同类型任务的执行顺序。在实际开发中,合理选择异步编程技术、遵循最佳实践,可以帮助我们编写出高效、可靠和易于维护的异步代码。

相关文章:

JavaScript系列03-异步编程全解析

本文介绍了异步相关的内容,包括: 回调函数与回调地狱Promise详解async/await语法Generator函数事件循环机制异步编程最佳实践 1、回调函数与回调地狱 JavaScript最初是为处理网页交互而设计的语言,异步编程是其核心特性之一。最早的异步编…...

Linux学习——退出vi编辑模式

初学Linux的时候,在使用vi 操作时候,有时候可能进入的是一个文件夹,这样子在退出的时候很不好操作! 下面总结一些vi 退出命令,学习! 进入编辑模式,按 o 进行编辑 编辑结束,按ESC 键 跳到命令…...

第2章_保护您的第一个应用程序

第2章_保护您的第一个应用程序 在本章中,您将学习如何使用 Keycloak 保护您的第一个应用程序。为了让事情更有趣,您将运行的示例应用程序由两部分组成,前端 Web 应用程序和后端 REST API。这将向您展示用户如何向前端进行身份验证&#xff0…...

【AGI】DeepSeek开源周:The whale is making waves!

DeepSeek开源周:The whale is making waves! 思维火花引言一、DeepSeek模型体系的技术演进1. 通用语言模型:DeepSeek-V3系列2. 推理优化模型:DeepSeek-R1系列3. 多模态模型:Janus系列 二、开源周三大工具库的技术解析1…...

Unity中动态切换光照贴图的方法

关键代码:LightmapSettings.lightmaps lightmapDatas; LightmapData中操作三张图:lightmapColor,lightmapDir,以及一张ShadowMap 这里只操作前两张: using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public cl…...

第三十四:6.4.【v-model】

6.4.【v-model】&#xff1a;双向绑定 概述&#xff1a;实现 父↔子 之间相互通信。 前序知识 —— v-model的本质 <!-- 使用v-model指令 --> <input type"text" v-model"userName"> ​ <!-- v-model的本质是下面这行代码 --> <inpu…...

React底层常见的设计模式

在React中&#xff0c;常见的设计模式为开发者提供了结构化和可重用的解决方案&#xff0c;有助于提高代码的可维护性和可扩展性。以下是对React中几种常见设计模式的详细解析&#xff0c;并附上示例代码和注释&#xff1a; 1. 容器组件与展示组件模式&#xff08;Container/P…...

从零基础到通过考试

1. 学习资源与实践平台 使用Proving Grounds进行靶机练习 OSCP的备考过程中&#xff0c;实战练习占据了非常重要的地位。Proving Grounds&#xff08;PG&#xff09;是一个由Offensive Security提供的练习平台&#xff0c;拥有152个靶机&#xff0c;涵盖了从基础到进阶的多种…...

UniApp 按钮组件 open-type 属性详解:功能、场景与平台差异

文章目录 引言一、open-type 基础概念1.1 核心作用1.2 通用使用模板 二、主流 open-type 值详解2.1 contact - 客服会话功能说明平台支持代码示例 2.2 share - 内容转发功能说明平台支持注意事项 2.3 getUserInfo - 获取用户信息功能说明平台支持代码示例 2.4 getPhoneNumber -…...

【无标题】ABP更换MySql数据库

原因&#xff1a;ABP默认使用的数据库是sqlServer&#xff0c;本地没有安装sqlServer&#xff0c;安装的是mysql&#xff0c;需要更换数据库 ABP版本&#xff1a;9.0 此处以官网TodoApp项目为例 打开EntityFrameworkCore程序集&#xff0c;可以看到默认使用的是sqlServer&…...

大模型微调入门(Transformers + Pytorch)

目标 输入&#xff1a;你是谁&#xff1f; 输出&#xff1a;我们预训练的名字。 训练 为了性能好下载小参数模型&#xff0c;普通机器都能运行。 下载模型 # 方式1&#xff1a;使用魔搭社区SDK 下载 # down_deepseek.py from modelscope import snapshot_download model_…...

【开源免费】基于SpringBoot+Vue.JS网络海鲜市场系统(JAVA毕业设计)

本文项目编号 T 222 &#xff0c;文末自助获取源码 \color{red}{T222&#xff0c;文末自助获取源码} T222&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...

在线会议时, 笔记本电脑的麦克风收音效果差是为什么

背景 最近在线面试. 使用腾讯会议或者飞书, 戴耳机参加在线面试, 遇到好几个面试官说我的音质不好. 一直没在意, 后来反思, 应该是电脑哪里出了问题. 排查 先买了一副品牌有线耳机, 测试后本地录制的声音仍然品质很差去掉耳机延长线后, 麦克风品质仍然很差最终找到答案, 原…...

理解文件系统

目录 文件系统 内存文件与磁盘文件的区别 初识inode 磁盘的概念 磁盘分区与格式化介绍 EXT2文件系统的存储方案 软硬链接 软连接 ​编辑 硬链接 软硬链接的区别 文件的三个时间 文件系统 内存文件与磁盘文件的区别 我们知道文件可以分为磁盘文件和内存文件&#…...

第二十四:5.2【搭建 pinia 环境】axios 异步调用数据

第一步安装&#xff1a;npm install pinia 第二步&#xff1a;操作src/main.ts 改变里面的值的信息&#xff1a; <div class"count"><h2>当前求和为&#xff1a;{{ sum }}</h2><select v-model.number"n">  // .number 这里是…...

Vue2+Element实现Excel文件上传下载预览【超详细图解】

目录 一、需求背景 二、落地实现 1.文件上传 图片示例 HTML代码 业务代码 2.文件下载 图片示例 方式一&#xff1a;代码 方式二&#xff1a;代码 3.文件预览 图片示例 方式一&#xff1a;代码 方式二&#xff1a;代码 一、需求背景 在一个愉快的年后&#xff…...

C# 装箱(Boxing)与拆箱(Unboxing)

C# 装箱&#xff08;Boxing&#xff09;与拆箱&#xff08;Unboxing&#xff09; 在 C# 中&#xff0c;装箱和拆箱是与值类型&#xff08;如结构体&#xff09;和引用类型&#xff08;如类&#xff09;之间的转换相关的操作。它们是类型系统的一部分&#xff0c;但如果不正确使…...

【AD】3-10 原理图PDF导出

文件—智能PDF 多页原理图导出 导出设置时选择工程&#xff0c;可自行选择导出一页或多页原理图&#xff0c;一般PCB不用导出...

SQL命令详解之增删改数据

目录 简介 1 添加数据 1.1 基础语法 1.2 SQL 练习 2 修改数据 2.1 基础语法 2.2 SQL 练习 ​3 删除数据 3.1 基础语法 3.2 SQL 练习 总结 简介 在数据库操作中&#xff0c;增、删、改是最基础的操作&#xff0c;它们通常对应着SQL中的INSERT、DELETE和UPDATE命令。…...

Docker 部署 MinIO 对象存储服务

Docker 部署 MinIO 对象存储服务 前言一、准备工作1. 安装 Docker2. 确认服务器架构 二、设置 MinIO 容器的目录结构三、启动一个临时的 MinIO 容器来获取配置文件四、复制 MinIO 配置文件到本地目录五、删除临时 MinIO 容器六、创建并运行 MinIO 容器&#xff0c;挂载本地目录…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践

前言&#xff1a;本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中&#xff0c;跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南&#xff0c;你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案&#xff0c;并结合内网…...

CppCon 2015 学习:Time Programming Fundamentals

Civil Time 公历时间 特点&#xff1a; 共 6 个字段&#xff1a; Year&#xff08;年&#xff09;Month&#xff08;月&#xff09;Day&#xff08;日&#xff09;Hour&#xff08;小时&#xff09;Minute&#xff08;分钟&#xff09;Second&#xff08;秒&#xff09; 表示…...