闲谈Promise
预备知识
- 回调函数:当一个函数作为参数传入另一个函数中,并且它不会立刻执行,当满足一定条件之后,才会执行,这种函数称为回调函数。比如:定时器。
- 异步任务:与之对应的概念是同步任务,同步任务是在主线程上排队执行,当前面的任务执行完成,才会执行下一个任务。异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完成不影响下一个任务的指向。简单理解就是同步要按代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。
为什么出现Promise
存在异步任务的代码,不能保证代码按照顺序执行,但在特定的需要下,就是需要在异步中实现顺序执行,这时常使用回调函数去实现,比如下面的代码:
setTimeout(()=>{console.log('找实习');setTimeout(()=>{console.log('面试通过');setTimeout(()=>{console.log('拿offer');setTimeout(()=>{console.log('上班');},2000)},1000)},1000)},2000)
执行结果:

虽然上面的代码能实现我们想要的结果,但一些情形下,需要嵌套更多回调函数时,多层嵌套让代码可读性非常差,后期的维护或者异常处理等都变得特别繁琐,让缩进格式也变得非常麻烦。
回调函数A中嵌套回调函数B,回调函数B中嵌套回调函数C.......这种回调函数多层嵌套的情况被称为回调地狱。回调地狱就是为了实现在异步任务中代码也能顺序执行而出现的一种操作。
为解决回调地狱带来的问题,ES6新增了Promise类,让我们能更优雅的书写复杂的异步任务。
Promise
先用一段代码认识一下Promise:
new Promise((resolve,reject)=>{setTimeout(()=>{resolve('3秒后执行')},3000)
}).then(res=>{console.log(res);})
- 是一个类,需要使用new Promise来创建实例对象。
- promise有三种状态:pending(待定),fulfilled(已兑现),rejected(已拒绝)。fulfilled和rejected也可以称为已敲定。promise初始状态是pending,promise的状态转换有两条路:一条pending->fulfilled,一条pending->rejected。状态一旦改变,就不能再发生变化。
- 调用resolve()方法,pending状态会变成fulfilled状态,并且触发then()中第一个回调方法;调用reject()方法,pending状态会变成rejected状态,并且触发then()中第二个回调方法。
- promise有三个实例方法,then、catch、finally。其中catch和finally实际都是在调用then方法,只是它们往then中传递的参数不一样。
catch : then(undefined,reject的回调方法) finally : then(A回调,A回调) - promise的三个实例方法和六个静态方法都是返回一个新的promise。六个静态方法为:resolve、reject、race、all、allSettled、any。其中race、all、allSettled、any将一个 Promise 可迭代对象作为输入(也可以简单理解成:传递数组作为参数)。
promise的实例方法
下面会对三个实例方法进行重写,对于相同的代码进行了提取:
// 将方法放到任务队列中的方法function runAsynctask(callback) {if (typeof queueMicrotask === "function") {queueMicrotask(callback);} else if (typeof MutationObserver === "function") {// 创建观察器const obs = new MutationObserver(callback);// 创建元素,添加监听const divNode = document.createElement("div");// 参数1 观察的dom节点// 参数2 观察的选项(childList 观察子节点改变)obs.observe(divNode, { childList: true });// 修改元素内容divNode.innerText = "999999";} else {setTimeout(callback, 0);}}// 对then不同的返回值进行不同操作function judgmentFn(x, p, resolve, reject) {if (x === p) {throw new TypeError("Chaining cycle detected for promise #<Promise>");}if (x instanceof QPromise) {x.then((res) => resolve(res),(err) => reject(err));} else {resolve(x);}}
then方法
then() 方法最多接受两个参数:用于 Promise 兑现和拒绝情况的回调函数。它返回一个 Promise 对象。
手写then方法:
then(onFulfilled, onRejected) {// 参数判断onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x) => x;onRejected = typeof onRejected === "function" ? onRejected : (x) => {throw x;};const p = new QPromise((resolve, reject) => {// 执行方法if (this.state === FULFILLED) {runAsynctask(() => {try {const x = onFulfilled(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});} else if (this.state === REJECTED) {runAsynctask(() => {try {const x = onRejected(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});} else if (this.state === PENDING) {// 保存回调函数this.#handlers.push({onFulfilled: () => {runAsynctask(() => {try {const x = onFulfilled(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});},onRejected: () => {runAsynctask(() => {try {const x = onRejected(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});},});}});return p;}
catch方法
catch() :在 promise 被拒绝时调用catch()里的函数;返回一个 Promise 对象。此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。
catch(onRejected) {// 内部调用then方法return this.then(undefined, onRejected);
}
finally方法
finally() :用于注册一个在 promise 敲定(兑现或拒绝)时调用的函数,无论是兑现还是拒绝,都是调用同一个函数。
finally(onFinally) {return this.then(onFinally, onFinally);}
promise的静态方法
resolve方法
Promise.resolve() :将给定的值转换为一个 Promise。如果这个值本身就是promise,则直接返回这个值(就算这个promise的状态是已拒绝的);其它值就封装成promise,返回的 Promise 将会以该值兑现。
static resolve(value) {// 如果是promise 直接返回if (value instanceof QPromise) {return value;}// 其他值,就直接封装成promise返回return new QPromise((res) => {res(value);});
}
reject方法
reject():返回一个已拒绝的 Promise 对象,拒绝原因 是给定的参数。
static reject(value) {// 不管是什么值(包括 Promise 对象),都重新封装成promise返回return new QPromise((undefined, reject) => {reject(value);});
}
race方法
race():等待第一个敲定;返回一个 Promise,这个返回的 promise 会随着第一个 promise 的敲定而敲定。
static race(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 等待第一个敲定(promise状态的敲定)promises.forEach((p) => {// 对数组中的每一项使用resolve,可以保证每一项都是promise,所以每一项都可以使用thenQPromise.resolve(p).then((res) => {resolve(res);},(err) => {reject(err);});});});
}
allSettled方法
allSettled():等待全部敲定;当数组中的每一个都已敲定时,返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。
static allSettled(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 空数组直接兑现promises.length === 0 && resolve(promises);// 等待全部敲定// 记录结果const results = [];// 记录兑现次数let count = 0;promises.forEach((p, index) => {// 对数组中的每一项使用resolve,可以保证每一项都是promise,所以每一项都可以使用thenQPromise.resolve(p).then((res) => {// 用索引来添加元素,保证结果顺序和promise数组的顺序一致results[index] = { status: FULFILLED, value: res };// 判断全部兑现count++;// 通过兑现的次数进行判断,保证获取到所有的结果count === promises.length && resolve(results);},(err) => {results[index] = { status: REJECTED, reason: err };count++;count === promises.length && resolve(results);});});});}
all方法
all():两种情况:
- 当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是空数组),并返回一个包含所有兑现值的数组。
- 如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
static all(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 空数组直接兑现promises.length === 0 && resolve(promises);// 处理全部兑现// 记录结果const results = [];// 记录兑现次数let count = 0;promises.forEach((p, index) => {// 对数组中的每一项使用resolve,可以保证每一项都是promise,所以每一项都可以使用thenQPromise.resolve(p).then((res) => {// 用索引来添加元素,保证结果顺序和promise数组的顺序一致results[index] = res;// 判断全部兑现count++;// 通过兑现的次数进行判断,保证获取到所有的结果count === promises.length && resolve(results);},(err) => {// 处理第一个拒绝reject(err);});});});}
any方法
any():两种情况:
- 当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。
- 当所有输入 Promise 都被拒绝(包括空数组)时,它会以一个包含拒绝原因数组的AggregateError 拒绝。
static any(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 空数组直接兑现promises.length === 0 && reject(new AggregateError(promises,'All promises were rejected'))// 等待结果const errors=[]let count=0promises.forEach((p,index)=>{QPromise.resolve(p).then(res=>{// 第一个兑现resolve(res)},err=>{// 全部拒绝errors[index]=errcount++count===promises.length && reject(new AggregateError(errors,'All promises were rejected'))})})});}
手写Promise
手写promise的完整代码
// 三个状态const PENDING = "pending";const FULFILLED = "fulfilled";const REJECTED = "rejected";// 定义QPromise类class QPromise {// 初始状态state = PENDING;// 初始原因result = undefined;// 定义实例属性#handlers = [];// 构造函数constructor(fn) {const resolve = (result) => {// 状态不可逆if (this.state === PENDING) {// 修改状态this.state = FULFILLED;this.result = result;// 调用成功回调this.#handlers.forEach(({ onFulfilled }) => {onFulfilled(this.result);});}};const reject = (result) => {// 状态不可逆if (this.state === PENDING) {// 修改状态this.state = REJECTED;this.result = result;// 调用失败回调this.#handlers.forEach(({ onRejected }) => {onRejected(this.result);});}};try {// 执行fn(resolve, reject);} catch (error) {reject(error);}}// then :将方法放到微任务队列中then(onFulfilled, onRejected) {// 参数判断onFulfilled =typeof onFulfilled === "function" ? onFulfilled : (x) => x;onRejected =typeof onRejected === "function"? onRejected: (x) => {throw x;};const p = new QPromise((resolve, reject) => {// 执行方法if (this.state === FULFILLED) {runAsynctask(() => {try {const x = onFulfilled(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});} else if (this.state === REJECTED) {runAsynctask(() => {try {const x = onRejected(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});} else if (this.state === PENDING) {// 保存回调函数this.#handlers.push({onFulfilled: () => {runAsynctask(() => {try {const x = onFulfilled(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});},onRejected: () => {runAsynctask(() => {try {const x = onRejected(this.result);judgmentFn(x, p, resolve, reject);} catch (error) {reject(error);}});},});}});return p;}// 实例方法catch(onRejected) {// 内部调用then方法return this.then(undefined, onRejected);}// 实例方法finally(onFinally) {return this.then(onFinally, onFinally);}// 静态方法static resolve(value) {// 如果是promise 直接返回if (value instanceof QPromise) {return value;}// 其他值,就直接封装成promise返回return new QPromise((res) => {res(value);});}static reject(value) {// 不管是什么值(包括 Promise 对象),都重新封装成promise返回return new QPromise((undefined, reject) => {reject(value);});}// 等待第一个敲定static race(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 等待第一个敲定(promise状态的敲定)promises.forEach((p) => {QPromise.resolve(p).then((res) => {resolve(res);},(err) => {reject(err);});});});}// 全部状态为成功,则返回一个数组// 若有一个失败,则处理失败static all(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 空数组直接兑现promises.length === 0 && resolve(promises);// 处理全部兑现// 记录结果const results = [];// 记录兑现次数let count = 0;promises.forEach((p, index) => {QPromise.resolve(p).then((res) => {// 用索引来添加元素,保证结果顺序和promise数组的顺序一致results[index] = res;// 判断全部兑现count++;// 通过兑现的次数进行判断,保证获取到所有的结果count === promises.length && resolve(results);},(err) => {// 处理第一个拒绝reject(err);});});});}// 等待全部状态敲定,不管是失败还是成功状态,都放在一个数组中返回static allSettled(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 空数组直接兑现promises.length === 0 && resolve(promises);// 等待全部敲定// 记录结果const results = [];// 记录兑现次数let count = 0;promises.forEach((p, index) => {QPromise.resolve(p).then((res) => {// 用索引来添加元素,保证结果顺序和promise数组的顺序一致results[index] = { status: FULFILLED, value: res };// 判断全部兑现count++;// 通过兑现的次数进行判断,保证获取到所有的结果count === promises.length && resolve(results);},(err) => {results[index] = { status: REJECTED, reason: err };count++;count === promises.length && resolve(results);});});});}// 返回第一个状态兑现(成功)的结果// 全部拒绝时,返回拒绝的结果数组static any(promises) {// 返回promisereturn new QPromise((resolve, reject) => {// 判断是否为数组if (!Array.isArray(promises)) {return reject(new TypeError("Argument is not iterable"));}// 空数组直接兑现promises.length === 0 && reject(new AggregateError(promises,'All promises were rejected'))// 等待结果const errors=[]let count=0promises.forEach((p,index)=>{QPromise.resolve(p).then(res=>{// 第一个兑现resolve(res)},err=>{// 全部拒绝errors[index]=errcount++count===promises.length && reject(new AggregateError(errors,'All promises were rejected'))})})});}}// 异步函数function runAsynctask(callback) {if (typeof queueMicrotask === "function") {queueMicrotask(callback);} else if (typeof MutationObserver === "function") {// 创建观察器const obs = new MutationObserver(callback);// 创建元素,添加监听const divNode = document.createElement("div");// 参数1 观察的dom节点// 参数2 观察的选项(childList 观察子节点改变)obs.observe(divNode, { childList: true });// 修改元素内容divNode.innerText = "999999";} else {setTimeout(callback, 0);}}// 对then不同的返回值进行不同操作function judgmentFn(x, p, resolve, reject) {if (x === p) {throw new TypeError("Chaining cycle detected for promise #<Promise>");}if (x instanceof QPromise) {x.then((res) => resolve(res),(err) => reject(err));} else {resolve(x);}}
如果本文对你有帮助,希望能得到你的点赞或收藏或关注,这是对我最好的鼓励;
如你有问题或疑惑,欢迎在评论区写下,必将努力解答;
如本文有误区,希望你不吝赐教,让我们共勉!
相关文章:
闲谈Promise
预备知识 回调函数:当一个函数作为参数传入另一个函数中,并且它不会立刻执行,当满足一定条件之后,才会执行,这种函数称为回调函数。比如:定时器。异步任务:与之对应的概念是同步任务࿰…...
【C++堆(优先队列)】1882. 使用服务器处理任务|1979
本文涉及知识点 C堆(优先队列) LeetCode1882. 使用服务器处理任务 给你两个 下标从 0 开始 的整数数组 servers 和 tasks ,长度分别为 n 和 m 。servers[i] 是第 i 台服务器的 权重 ,而 tasks[j] 是处理…...
VBA高级应用30例应用3Excel中的ListObject对象:选择表的一部分
《VBA高级应用30例》(版权10178985),是我推出的第十套教程,教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开,这套教程案例与理论结合,紧贴“实战”,并做“战术总结”,以…...
C语言-变量
全局变量可以定义在头文件中吗? 在C和C编程中,全局变量可以定义在头文件中,但通常不建议这样做,因为这可能导致多个源文件(.c 或 .cpp 文件)包含同一个头文件时,发生多重定义错误(m…...
linux下位机出现使用TCP socket为0的问题
问题现象:下位机做TCP服务器,上位机来连接下位机的TCP服务,中间会有主动断开(上位机主动关闭socket)和异常断开(网线断开)的情况,出现异常的时候,上位机连接下位机的TCP …...
论文笔记:Prototypical Verbalizer for Prompt-based Few-shot Tuning
论文来源:ACL 2022 论文地址:https://arxiv.org/pdf/2203.09770.pdfhttps://arxiv.org/pdf/2203.09770.pdf 论文代码:https://github.com/thunlp/OpenPrompthttps://github.com/thunlp/OpenPrompt Abstract 基于提示的预训练语言模型&#…...
nn.functional.softmax(X, dim=-1)
dim-1表示在最后一个维度(大概率是一行)应用Softmax函数,将值标准化为概率分布。 实例 假设我们有一个张量X,形状为(2,3),内容如下: import torch import torch.nn.…...
【动态规划】子数组系列(上)
1. 最大子数组和 53. 最大子数组和 状态表示:以 i 位置为结尾时的所有子数组中的最大和 状态转移方程: i 位置为结尾的子数组又可以分为长度为 1 的和大于 1 的,长度为 1 就是 nums[i] ,长度不为 1 就是 dp[i - 1] nums[i]&…...
字节青训营入门算法题:飞行棋分组
链接:飞行棋分组🔗🔗 题目 现在有一堆飞行棋棋子,每个棋子上标有数字序号。需要将这些棋子分成若干组,每组包含5个棋子,且组内所有棋子的数字序号必须相同。需要判断是否可以完成这样的分组。 解答 为了…...
# 执行 rpm -qa | grep qq 查询软件安装情况时报错 数据库损坏 db3 error(-30974)
执行 rpm -qa | grep qq 查询软件安装情况时报错 数据库损坏 db3 error(-30974) 一、问题描述: 在 linux 系统上,使用包管理工具 rpm 查询某一个软件安装情况,如:执行 rpm -qa | grep qq 时,报错 数据库损坏 db3 err…...
离线服务器上复现G3SR论文实验
代码地址:https://github.com/AllminerLab/Code-for-G3SR-master 论文地址:https://ieeexplore.ieee.org/abstract/document/9741079/ 因为直接按照作者的方法操作会出现问题,故笔者在这里记录一下的实验过程。 实验环境 python=3.6 pytorch pytorch的下载命令需要自行前往…...
Android 未来可能支持 Linux 应用,Linux 终端可能登陆 Android 平台
近日,根据 android authority 的消息,Google 正在开发适用于 Android 的 Linux 终端应用,而终端应用可以通过开发人员选项启用,并将 Debian 安装在虚拟机中。 在几周前,Google 的工程师开始为 Android 开发新的 Termi…...
PostgreSQL学习笔记十四:PL/Python自定义函数
在 PostgreSQL 中可以使用 PL/Python 语言来创建自定义函数。以下是一个示例步骤: 一、创建自定义函数 连接到 PostgreSQL 数据库,可以使用 psql 命令行工具或者通过数据库管理工具。 执行以下 SQL 语句创建一个简单的 PL/Python 函数: C…...
计算机毕业设计 | springboot商城售后管理系统 购物平台(附源码)
1,绪论 1.1 开发背景 在数字化时代的推动下,产品售后服务管理机构面临着信息化和网络化的挑战。传统的手工管理和纸质档案已经无法满足管理人员和读者的需求。为了提高产品售后服务管理机构的管理效率和服务质量,开发和实现一个基于Java的售…...
(全网独家)面试要懂运维真实案例:HDFS重新平衡(HDFS Balancer)没触发问题排查
在面试时,面试官为了考察面试者是否真的有经验,经常会问运维集群时遇到什么问题,解决具体流程。下面是自己遇到HDFS Balancer没执行,花了半天时间进行排查,全网独家的案例和解决方案。 目录 使用CDH自带重新平衡操作…...
【数据结构笔记】搜索树
二叉搜索树 任一节点x的左/右子树中,所有非空节点均不大于(不小于)x 必须是所有的非空节点,仅左右孩子不够(左孩子的右孩子可能很大)一棵二叉树是二叉搜索树当且仅当中序遍历序列是单调非降序列 两棵二叉…...
如何使用UART(STM32 HAL库)
UART (通用异步收发器)是在 USART (通用同步异步收发器)基础上裁剪掉了同步通信功能,只剩下异步通信功能。关于通信和串口的基本知识,可参见文章《串口通信简介-CSDN博客》和《数据通信的一些基础概念-CSDN…...
星巴克英语
用流利的英文点星巴克 一杯咖啡 英文中文英文中文barista咖啡师coffee maker家用咖啡机cup sleeve杯套coffee stirrer咖啡棒coffee cup lid咖啡杯盖子straw吸管latte art咖啡拉花for here内用to go外带 例句: Could I have a cup sleeve for my coffee , please…...
权重衰减与暂退法——paddle部分
权重衰减与暂退法——paddle部分 本文部分为paddle框架以及部分理论分析,torch框架对应代码可见权重衰减与暂退法torch import paddle print("paddle version:",paddle.__version__)paddle version: 2.6.1当我们谈论机器学习模型的性能时,经…...
golang获取当天最小的时间,以DateTime的string格式返回
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...
Chrome扩展开发实战:打造浏览器侧边栏ChatGPT助手
1. 项目概述:一个让ChatGPT常驻浏览器侧边栏的利器如果你和我一样,每天的工作和学习都离不开浏览器,并且频繁地与ChatGPT对话来获取灵感、润色文案或者调试代码,那么你肯定对在无数个标签页之间来回切换感到厌烦。每次都要打开一个…...
Java 大厂面试 200 题完整版含答案解析
前言本文整理了近两年从阿里、腾讯、字节、美团、京东、拼多多等大厂面试中高频出现的 200 道 Java 面试题,覆盖 Java 基础、集合、并发、JVM、Spring、MySQL、Redis、消息队列、分布式、场景设计 等核心模块,每题都附有简明扼要的答案解析,助…...
从零构建团队技能仓库:结构化知识管理与VuePress实践
1. 项目概述:一个技能仓库的诞生与价值 最近在整理团队内部的技术资产时,我一直在思考一个问题:如何让那些散落在个人笔记、项目代码片段、会议纪要里的“隐性知识”和“最佳实践”沉淀下来,变成团队可复用、可传承的“显性资产”…...
如何在Windows上无缝安装安卓应用:APK安装器终极指南
如何在Windows上无缝安装安卓应用:APK安装器终极指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否曾在电脑上羡慕安卓应用的便利,却苦…...
Otter多模态大模型实战:从架构解析到部署应用的完整指南
1. 项目概述:当多模态大模型学会“看”与“说”最近在开源社区里,一个名为Otter的多模态大模型项目引起了我的注意。它来自EvolvingLMMs-Lab,这个实验室的名字就很有意思,“Evolving LMMs”—— 进化中的大型多模态模型。Otter 这…...
Python Pydantic介绍(数据校验、自动类型转换、结构化数据建模、序列化JSON、配置管理)pydantic-settings、核心BaseModel、字段约束Field()、FastAPI
文章目录Python 数据校验神器:Pydantic 完全指南一、什么是 Pydantic二、Pydantic 能解决什么问题1)数据校验(Validation)2)自动类型转换(Parsing)3)结构化数据建模4)序列…...
多智能体系统架构设计:从核心原理到AgentOrg工程实践
1. 项目概述:从“AgentOrg”看智能体组织架构的工程实践最近在开源社区里看到一个挺有意思的项目,叫“Angelopvtac/AgentOrg”。光看这个名字,可能有点抽象,但如果你正在捣鼓大语言模型应用,尤其是想构建一个能协同工作…...
Adafruit Bluefruit模块DFU模式恢复与固件更新全攻略
1. 项目概述如果你正在玩Adafruit的Bluefruit系列蓝牙模块,比如UART Friend或者SPI Friend,并且某天它突然“变砖”了——连接不上、没反应,或者Arduino IDE里怎么也刷不进新程序,先别急着把它扔进抽屉吃灰。这种情况我遇到过不止…...
为什么你的ElevenLabs马拉雅拉姆文输出失真?5步诊断法+3个预处理Python脚本立即修复
更多请点击: https://intelliparadigm.com 第一章:ElevenLabs马拉雅拉姆文输出失真的根本成因 马拉雅拉姆语(Malayalam)作为印度喀拉拉邦的官方语言,拥有高度复杂的音节结构、连字规则(conjunct consonant…...
【实用小程序】超轻量级文件上传下载中心 (File Download Server)
站内源码及jar包下载 一、项目概述 文件下载中心一个基于 Java 内置 HTTP 服务器(com.sun.net.httpserver)构建的轻量级文件管理服务。它零第三方依赖,单 JAR 包即可运行,适合在内网环境或临时场景中快速搭建文件共享站点。 你的团队需要临时共享一批日志文件或交付物,…...
