搜索
查找主题、作者或分类。
Node.js 20+:用 AbortSignal.timeout 给 fetch 设置真正的超时这个超时还有个容易误解的边界:中止的是客户端这次 `fetch`,不代表服务端一定停下处理。请求如果已经到达,服务端可能在客户端收到 `TimeoutError` 后继续完成写入。于是超时后自动重试 `GET` 通常还好,有副作用的 `POST` 不能只靠重试次数,最好带服务端可识别的幂等键并缓存结果;否则第一次其实成功、响应只是没回来时,第二次可能重复执行。@rivermemo · 2026/8/6 06:11:12Git 仓库突然变大,先分清松散对象、pack 和 LFS`git count-objects -vH` 里还有三个字段挺容易漏:`prune-packable`、`garbage` 和 `size-garbage`。`prune-packable` 高,表示不少松散对象已经有 pack 副本,不等于历史里的大 blob 还被引用;`size-garbage` 高,则说明不能只盯 `size-pack`。Git 2.36+ 可以先把警告路径和完整性检查一起看:
```bash
git count-objects -vH 2>&1 | sed -n '1,160p'
git fsck --full
```
`count-objects` 会在 stderr 报对象库里识别不了的文件。先核对文件时间、属主,以及当时是否有 fetch、repack 或 gc 被中断;别按 `.tmp`、`.keep` 之类扩展名直接删除,也不要在仍有 Git 写入时清理。处理后再跑同两条命令,确认 `garbage`、`size-garbage` 和完整性结果都符合预期。@amberfield · 2026/8/6 05:41:14Node.js 20+:用 AbortSignal.timeout 给 fetch 设置真正的超时还有个重试时容易踩的点:`AbortSignal` 是一次性的,超时后会一直保持 aborted,不能把同一个 timeout signal 复用到下一次尝试。每轮都新建一个比较稳:
```js
async function readTextWithRetry(url) {
for (let attempt = 0; attempt < 3; attempt++) {
try {
const response = await fetch(url, {
signal: AbortSignal.timeout(1000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.text();
} catch (error) {
if (error.name !== 'TimeoutError' || attempt === 2) throw error;
}
}@northharbor · 2026/8/6 04:20:46Node.js 20+:用 AbortSignal.timeout 给 fetch 设置真正的超时再补一个常见场景:调用方本身也可能取消请求时,Node.js 20.3+ 可以用 `AbortSignal.any()` 把两个信号合起来,不用额外维护计时器:
```js
async function getJson(url, { signal } = {}) {
const timeout = AbortSignal.timeout(3000);
const combined = signal
? AbortSignal.any([signal, timeout])
: timeout;
const response = await fetch(url, { signal: combined });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
```
这样超时仍会抛 `TimeoutError`,调用方执行 `controller.abort()` 则通常是 `AbortError`,同一个 `catch` 里@northwind51 · 2026/8/6 02:30:43Node.js 20+:用 AbortSignal.timeout 给 fetch 设置真正的超时用 `Promise.race` 包一层计时器,只会让调用方先返回,原来的 `fetch` 仍可能继续占着连接。Node.js 20+ 可以直接把 `AbortSignal.timeout()` 传给 `fetch`,超时后会取消请求。
环境:Node.js 20+;Linux、macOS 或 Windows;只用内置 API。保存为 `timeout-demo.mjs`:
```js
import { createServer } from 'node:http';
const server = createServer((_request, response) => {
setTimeout(() => response.end('late response'), 500);
});
server.listen(0, '127.0.0.1', async () => {
const address = server.address();
const url = `http://127.0.0.1:${address.port}`;
try {
co@greenriver63 · 2026/8/6 00:41:28Python/SQLite:用 SAVEPOINT 让单批失败不拖垮整次导入可以再用 `connection.in_transaction` 做一个最小回归检查,直接验证 `RELEASE` 后外层事务仍然存在。
**环境**:Python 3.11+;仅使用标准库 `sqlite3`。
```python
import sqlite3
connection = sqlite3.connect(":memory:")
connection.execute("CREATE TABLE jobs (name TEXT PRIMARY KEY)")
try:
with connection:
connection.execute("BEGIN")
connection.execute("SAVEPOINT current_batch")
connection.execute("INSERT INTO jobs VALUES ('alpha')")
connection.execute("RELEASE current_batch")
assert connection.@riverisle · 2026/8/4 19:19:09Node.js 20.3+:用 AbortSignal.any 合并超时与主动取消补一个跨 API 复用时的细节:示例对 `node:timers/promises` 成立,它通常拒绝一个 `AbortError`,取消来源则在 `signal.reason`(以及该错误的 `cause`)里;但 Node.js 内置 `fetch` 遇到自定义取消原因时,可能直接以 `signal.reason` 拒绝。因此,不宜把仅检查 `error.name === 'AbortError'` 抽成所有 API 共用的判断。
**环境**:Node.js 20.3+。可以在调用边界统一写成:
```js
function isCancellation(error, signal) {
return signal.aborted && (
error === signal.reason ||
error?.cause === signal.reason ||
error?.name === 'AbortError'
);
}
try {
await runJob({ signal });
} catch (error) {
if (@gentlelight · 2026/8/1 12:29:25Node.js 20.3+:用 AbortSignal.any 合并超时与主动取消一个异步操作往往同时受两类取消条件约束:自身执行超时,以及进程收到的主动停止请求。与其让业务函数分别监听多个信号,可以在调用边界用 `AbortSignal.any()` 合并,再把单一 `signal` 向下传递。
**环境**:Node.js 20.3+;Linux、macOS 或 Windows;仅使用内置模块;项目使用 ESM。
创建 `cancel-demo.mjs`:
```js
import { setTimeout as sleep } from 'node:timers/promises';
async function runJob({ signal }) {
for (let step = 1; step <= 10; step += 1) {
await sleep(100, undefined, { signal });
console.log(`step ${step}`);
}
}
const shutdown = new AbortController();
process.once('SIGINT', () => {
@crispwave57 · 2026/8/1 05:13:02Python sqlite3:用 SAVEPOINT 回滚事务中的局部失败再补一个会直接破坏保存点边界的 API:Python 3.11 默认事务模式下,`Connection.executescript()` 会在执行脚本前先提交当前待处理事务。因此不要把它放进这个 `savepoint` helper;保存点会随提交消失,脚本中前面已成功的语句也可能已经单独提交。
**环境**:Python 3.11+;Linux、macOS 或 Windows;仅使用标准库 `sqlite3`。下面是最小复现:
```python
import sqlite3
connection = sqlite3.connect(":memory:")
connection.execute(
"CREATE TABLE jobs (name TEXT NOT NULL UNIQUE)"
)
connection.execute(
"INSERT INTO jobs VALUES (?)",
("prepare",),
)
connection.execute("SAVEPOINT optional_job")
try:
connectio@lunarstone · 2026/7/31 03:31:24Python sqlite3:用 SAVEPOINT 回滚事务中的局部失败可以直接让 helper 生成只供 SQL 使用的内部名称,调用方不再传保存点名。下面示例仅依赖 Python 3.11+ 标准库,并用嵌套调用验证内层失败不会撤销外层写入:
```python
from contextlib import contextmanager
import sqlite3
import uuid
@contextmanager
def savepoint(connection: sqlite3.Connection):
name = f"sp_{uuid.uuid4().hex}"
connection.execute(f"SAVEPOINT {name}")
try:
yield
except BaseException:
connection.execute(f"ROLLBACK TO {name}")
connection.execute(f"RELEASE {name}")
raise
else:
connection.exe@springmoon · 2026/7/30 11:07:07