JavaScript forEach 不支持 await:for...of 顺序执行与 Promise.all 并发执行
Array.prototype.forEach 不会等待异步回调完成,这是 JavaScript 里最常见的异步陷阱之一。 问题演示 const arr = [1, 2, 3];arr.forEach(async (item) => { await sleep(1000); console.log(item); });console.log('结束');输出顺序是: 结束 1 2 3forEach 本身是同步的,不关心回调返回的是否是 Promise,调用后立即继续执行。 await arr.forEach(...) 也没有意义,因为 forEach 的返回值始终是 undefined: await undefined // 无效等待顺序执行:用 for...of for (const item of arr) { await doSomething(item); }特点:一个完成后再执行下一个,顺序稳定。适合:限流的 API 请求(防止并发过多) 数据库写入(避免竞争) 依赖上一步结果的操作并发执行:用 Promise.all + map await Promise.all( arr.map(async (item) => { await doSomething(item); }) );特点:全部同时启动,等所有完成。适合独立任务批量执行,注意不要把接口打爆。 限制并发数量(每次最多 N 个): async function runConcurrent(arr, fn, concurrency = 5) { const results = []; for (let i = 0; i < arr.length; i += concurrency) { const batch = arr.slice(i, i + concurrency); results.push(...await Promise.all(batch.map(fn))); } return results; }选择参考场景 推荐写法顺序 await,依赖上一步 for...of全部并发,互不依赖 Promise.all + map不需要等待结果 forEach(此时没问题)requestIdleCallback:避免长循环阻塞主线程 如果是大量数据的 CPU 密集计算(如点云、坐标变换),不要在主线程单次执行,而是用 requestIdleCallback 分片处理: function processChunked(data, chunkSize = 1000) { return new Promise((resolve) => { let i = 0; const results = []; function step(deadline) { while (i < data.length && deadline.timeRemaining() > 0) { results.push(expensiveOp(data[i])); i++; } if (i < data.length) { requestIdleCallback(step); } else { resolve(results); } } requestIdleCallback(step); }); }特点:浏览器空闲时处理,不阻塞动画和用户交互。Safari 不支持 requestIdleCallback,可以用 setTimeout(fn, 0) 作为降级方案。 一句话总结 涉及 await 就别用 forEach,用 for...of(顺序)或 Promise.all(并发)。
Electron asar 解包:资源为什么只有一部分以及各类资源的位置
解包 app.asar 后只看到"部分资源",通常不是工具有问题,而是资源本来就不全在 asar 里。 app.asar.unpacked 目录 很多 Electron 应用把资源拆成两部分: resources/ ├─ app.asar ← 打包进归档的前端代码 └─ app.asar.unpacked ← 不打包,直接放文件系统不能放进 asar 的资源通常在 unpacked:.node 原生模块 dll、exe 二进制 ffmpeg 大模型权重 WASM 文件 Chromium 扩展解包命令: npx asar extract app.asar out查看归档内容: npx asar list app.asar运行时动态下载 很多 AI 软件、IDE 首次启动后会在线下载:JS chunk 模型权重 WASM 模块 加密资源这些完全不在 asar 里。常见存放位置: # Windows %AppData%\Roaming\应用名\ ← 配置、数据库、历史记录 %AppData%\Local\应用名\ ← 缓存、大文件、模型webpack 打包合并 现代 Electron 应用几乎不会保留 src/pages/ 这样的目录结构,而是打成 bundle: main.js renderer.js chunk.abc123.js你以为"缺文件",其实文件已经被合并进 bundle 了。搜索以下关键词确认: webpackChunk __webpack_require__加密和混淆 部分商业应用会:存 .jsc / .bin V8 字节码(bytenode),无法直接还原源码 webpack chunk 内字符串 XOR 混淆 运行时解密:decrypt(buffer); eval(...) 拿到真正的代码各类资源的位置速查目标 位置前端源码(React/Vue) app.asar 解包后的 bundle配置、token、sqlite %AppData%\Roaming\应用名缓存、模型、大文件 %AppData%\Local\应用名原生模块、二进制 app.asar.unpacked内联图片 bundle 里 base64 编码关键 IPC 桥接逻辑 preload.jsElectron 默认缓存目录 Cache/ Code Cache/ ← 可能有 V8 字节码缓存 GPUCache/ IndexedDB/ ← 配置、文档、AI 数据 Local Storage/ blob_storage/完整逆向流程 # 1. 解包 npx asar extract app.asar out# 2. 找 webpack 入口 grep -r "__webpack_require__" out/# 3. 重点看 preload # out/main/preload.js 或 dist-electron/preload.js# 4. 如果有加密,搜解密关键词 grep -r "decrypt\|Buffer\|crypto\|atob" out/# 5. 找用户数据 ls %AppData%\Roaming\应用名常见真实目录结构 MyApp/ ├─ resources/ │ ├─ app.asar │ ├─ app.asar.unpacked/ │ │ ├─ native.node │ │ └─ ffmpeg.dll │ └─ ... └─ locales/AppData/Roaming/MyApp/ ├─ IndexedDB/ ├─ Local Storage/ ├─ settings.json └─ user.db
Electron app.asar 解包只有部分资源的原因与逆向方法
为什么解包只有部分资源 1. 文件在 app.asar.unpacked 而不在 asar 里 Electron 会把不适合打包进 asar 的文件放到同级目录: resources/ ├─ app.asar ← 前端代码、JS bundle └─ app.asar.unpacked/ ← 原生模块、大文件通常放在 unpacked 里的有:.node 原生扩展 ffmpeg.dll / ffmpeg.so AI 模型、WASM 文件 浏览器内核组件解包时要把 app.asar 和 app.asar.unpacked 都看一遍。 2. webpack 把所有代码打成了 bundle 现代 Electron 应用几乎都用 webpack 打包,解包后看到的不是: src/pages/index.js src/components/Button.js而是: main.js renderer.js chunk.a1b2c3.js所有模块都合并进了 bundle,没有"缺文件",只是代码被合并了。 3. 资源在运行时从网络下载 部分 AI 软件、IDE 工具首次启动时会在线下载:语言包 插件资源 模型文件 JS chunk下载后存到用户目录,不在安装包里: Windows: %AppData%\应用名\ ← 配置、sqlite、token %LocalAppData%\应用名\ ← 缓存、大文件、下载资源4. JS 被编译为 V8 字节码(.jsc) 商业 Electron 应用常用 bytenode 把 JS 编译成 .jsc: main.js → main.jsc普通 asar 解包后可以看到文件,但无法读取源码。 5. 应用对资源做了加密 运行时解密: // 应用内部流程(无法直接看到) const encrypted = fs.readFileSync('xxx.bin'); const code = decrypt(encrypted, key); eval(code);解包后只能看到加密的 .bin 文件。 解包工具 # 查看 asar 文件列表 npx asar list app.asar# 解包到指定目录 npx asar extract app.asar ./out解包后搜索这些关键字找 webpack 入口: webpackChunk __webpack_require__Electron 应用资源分布位置 通常存放resources/app.asar 前端页面、JS bundle、html、cssresources/app.asar.unpacked/ .node 模块、dll、ffmpeg、WASM%AppData%/应用名/ 用户配置、sqlite、历史记录%LocalAppData%/应用名/ 缓存、下载的大文件、模型%AppData%/.../Code Cache/ V8 字节码缓存%AppData%/.../IndexedDB/ 结构化数据逆向流程 1. 解包 asar npx asar extract app.asar ./out2. 检查 app.asar.unpacked ls resources/app.asar.unpacked/3. 找 preload.js preload 是 Electron 主进程和渲染进程之间的桥梁,通常包含 IPC 通信和权限暴露逻辑: out/main/preload.js out/dist-electron/preload.js4. 找用户数据目录 # Windows dir %AppData%\应用名 dir %LocalAppData%\应用名5. Hook fs 抓解密内容 对于运行时解密的资源,hook fs.readFileSync 把解密后的结果 dump 出来: const fs = require('fs'); const orig = fs.readFileSync;fs.readFileSync = function (path, options) { const result = orig.apply(this, arguments); if (path.includes('.bin') || path.includes('.jsc')) { console.log('[readFileSync]', path, result.toString('hex').slice(0, 64)); } return result; };6. 检查是否是 V8 字节码 .jsc 文件开头有 V8 magic bytes,可以用 bytenode 的 wrap 机制在运行时拿到原始代码,但需要和目标 Node.js/Electron 版本完全匹配。
解包 Electron 的 app.asar 只有部分文件?六个原因
Electron 应用的 resources/app.asar 解开看,只有一部分源码 / 资源——很多明明代码里 require 过的模块或者用到的图片、模型完全找不到。不是解包工具坏,是 Electron 应用故意这么组织的。 1. 文件在 app.asar.unpacked 最常见。resources/ 目录下有两个: resources/ ├── app.asar # 归档 └── app.asar.unpacked/ # 不归档,直接文件系统electron-builder / electron-packager 有 asarUnpack 配置,指定哪些文件不打进 asar。原因:原生模块 .node:Node 加载 native addon 只认真实文件路径 可执行文件(ffmpeg.exe、外部工具) 大文件(模型、字体、图片)——放 asar 会导致启动时全部读到内存 需要写入的资源(缓存、配置模板)解决:同时解 asar 和 unpacked 目录,才能拿全资源。 npx asar extract app.asar out/ # 然后手动合并 app.asar.unpacked 里的内容2. 运行时动态下载 很多 AI / IDE / 客户端首次启动会拉模型或 SDK:Cursor / VSCode 下载语言服务 Notion / Figma 缓存 web 资源 Whisper.cpp 下运行 GGUF 模型 客户端更新组件到 %AppData%常见落地目录: Windows: %APPDATA%\<AppName>\ %LOCALAPPDATA%\<AppName>\macOS: ~/Library/Application Support/<AppName>/ ~/Library/Caches/<AppName>/Linux: ~/.config/<AppName>/ ~/.cache/<AppName>/这些目录不属于 asar,别在 asar 里找。 3. webpack chunk 打包 / 混淆 代码里看到: require("./modules/wallet.js")在 asar 里 modules/ 目录空空如也。原因是打包时被 webpack 摊平: (() => { var __webpack_modules__ = { 123: (e, t, r) => { /* wallet.js 的内容 */ }, 456: (e, t, r) => { /* something else */ }, }; // ... })();wallet.js 变成模块 ID 123,塞进主 bundle。名字都没了,找源文件是找不到的。 恢复思路:用 webcrack 或 react-native-decompiler 尝试还原模块结构 source map 有的话直接 .map 文件恢复 主 bundle 里搜函数名 / 字符串手工定位4. asar 完整性校验 Electron 的 --asar-integrity 特性(v22+)——启动时校验 asar 的 SHA-256,被改过就直接崩: Integrity check failed: 0x...有的应用还会自校验: const hash = crypto.createHash("sha256").update(fs.readFileSync(asarPath)).digest("hex"); if (hash !== EXPECTED_HASH) app.exit();改过 asar 塞回去,应用启动就退。想跑起来得同时改校验逻辑。 5. 资源加密 商业 Electron 应用常见做法: 方式 A:启动时解密 const encryptedBuffer = fs.readFileSync("./data.bin"); const source = decrypt(encryptedBuffer, KEY); new Function(source)(); // 或 eval, vm.runInNewContextasar 里只能看到加密后的 blob。想读得先拿到 KEY——通常在主 bundle 里硬编码或经过混淆。 方式 B:V8 Bytecode(bytenode) xxx.jsc xxx.bin这些是 bytenode 编译的 V8 字节码,普通反编译工具还原不了源码。只能在运行时逆向。 6. 解包工具版本太老 老的第三方解包工具不支持新版 asar 格式:新版 asar header 有 offset 大文件跨块 unicode 文件名编码变化永远优先用官方: # 官方 CLI npx @electron/asar extract app.asar out/ npx @electron/asar list app.asar或者装全局: npm i -g @electron/asar asar extract app.asar out/一张排查表 看到"资源缺",按这个顺序查:现象 大概率原因.node / ffmpeg 找不到 在 app.asar.unpacked模型 / 大图找不到 unpacked 或运行时下载require 的模块找不到源文件 webpack 打包混淆.jsc / .bin 打不开 V8 bytecode 或加密改了 asar 应用启动就退 完整性校验一部分目录空 / 文件小 解包工具太老,换 @electron/asar一句话总结 Electron 解包"少东西"是常态。先看 app.asar.unpacked、再看 %AppData% 用户目录、再考虑 webpack chunk / 加密 / bytecode。用 @electron/asar 官方工具,别用老的第三方版本。
Windows 安装 PostgreSQL:Installer / ZIP 手动安装 / Docker 三种方式
方式一:官方 EDB 安装包(推荐新手) 从 postgresql.org/download/windows 下载 EDB 安装器,双击 .exe 按向导操作:选择组件:PostgreSQL Server + pgAdmin + Command Line Tools(全选即可) 安装路径默认 C:\Program Files\PostgreSQL\16\ 设置 postgres 超级用户密码(这一步要记好,后面连接必须用) 端口保持默认 5432 Locale 保持默认 等待安装完成安装后验证: # 命令行里输入(要先配好 PATH) psql -U postgres -h localhost# 输入密码后看到: postgres=#或者打开 pgAdmin 4 用图形界面管理。 方式二:ZIP 手动安装 适合不想用安装包、想自定义路径的场景。 1. 解压到目标目录,如 D:\pgsql 2. 初始化数据目录: D:\pgsql\bin\initdb -D D:\pgsql\data -U postgres -W-W 参数会提示设置 postgres 用户密码,必须加,否则后续连接会失败。 3. 启动服务: D:\pgsql\bin\pg_ctl -D D:\pgsql\data -l D:\pgsql\logs\postgresql.log start4. 连接验证: D:\pgsql\bin\psql -U postgres -h localhost5. 把 bin 目录加入 PATH: 系统属性 → 环境变量 → Path → 新增: D:\pgsql\bin加好之后才能直接用 psql、pg_ctl 等命令。 EDB 安装包也会自动加 PATH,ZIP 安装需要手动配。 方式三:Docker(开发推荐) 一条命令,跳过所有安装步骤: docker run --name pgsql \ -e POSTGRES_PASSWORD=123456 \ -p 5432:5432 \ -d postgres连接: psql -U postgres -h localhost # 密码:123456停止/启动: docker stop pgsql docker start pgsql开发环境用 Docker 最省事,环境隔离,随时删掉重建。 常见问题 连不上 5432:防火墙没放行 → 允许 PostgreSQL 入站 端口冲突 → 改 postgresql.conf 里的 port = 5433忘记 postgres 密码: 修改 pg_hba.conf,把认证方式临时改成 trust,重启服务后进去改密码,再改回 scram-sha-256。 psql 找不到命令: PATH 没配好。把 PostgreSQL bin 目录加进去,或者用完整路径调用。 版本选择版本 建议PostgreSQL 17 新项目首选(最新稳定版)PostgreSQL 16 最稳定,生产首选PostgreSQL 15 及以下 老项目兼容,新项目不建议关于生产环境 生产环境强烈建议用 Linux(Ubuntu/Debian/Rocky Linux)跑 PostgreSQL:更好的 I/O 调度和文件系统(ext4/xfs) pgBackRest、Patroni 等高可用工具主要支持 Linux 运维脚本生态更成熟Windows 版 PostgreSQL 可以用,但长期高并发稳定性和性能不如 Linux。开发/测试环境没问题,生产环境能用 Linux 就用 Linux。
