今日已更新 160 条资讯 | 累计 24117 条内容
关于我们

标签:#javascript

找到 658 篇相关文章

AI 资讯

Reading a Paginated API Without Holding the Whole Thing in Memory

Your API hands out 50 records at a time across 400 pages. You need all of them. You do not need them all at once. Here's a very familiar situation that shows up constantly on the backend. Some API returns data in pages, 50 or 100 records at a time, and you need to walk every page: sync them to your database, export them to a file, run a report. The endpoint gives you a cursor or a page number and you keep asking until there's nothing left. The way most of us write it the first time looks like this: async function getAllRecords () { const all = []; let cursor = 0 ; while ( cursor !== null ) { const { records , nextCursor } = await fetchPage ( cursor ); all . push (... records ); cursor = nextCursor ; } return all ; } const everything = await getAllRecords (); for ( const record of everything ) { process ( record ); } It works. At four hundred records it's fine. The trouble starts when the dataset grows, and it has three separate problems hiding in it. It holds the entire dataset in memory before you touch a single record. It's all or nothing: if page 380 fails, you've thrown away the 19,000 records you already fetched . And it's eager. You can't start processing record one until the very last page has landed , even if all you wanted was the first ten. There's a shape in JavaScript built for exactly this, and if you read the first two posts in this series you already have both halves of it. Two ideas you've already seen In the CSV post , we pulled rows out of a huge file one at a time with a generator, so the file never fully loaded into memory. Lazy. Pull-based. You ask for the next row, you get the next row, nothing more. In the async/await post , we saw that a generator can pause at a yield and resume later.A generator can hold its place across an asynchronous gap. Put those together. A generator that pulls data lazily, and can pause to await something between pulls. That's an async generator, and it's the natural tool for walking a paginated API. You pull records

2026-06-13 原文 →
开发者

An Itty Bitty Aster Plotter problem...

Eight years ago (a geological epoch or two ago in Internet terms) Nicholas Jitkoff released itty.bitty.site - a website which could render whole websites just based on what was in the link, something like: itty.bitty.site?SOMEBASE64ENCODEDVALUE== et voilà! Free web-hosting if you could make it fit ;) At the time, I was rather obsessed with qr-codes thanks to developing QRGoPass and was working with aster plots a lot, so I developed an app that could fit into a qr-code! Today itty.bitty.site no longer exists so I can't do that any more... But I did make it 80% smaller without "cheating" and using modern CSS instead of d3.js ;)

2026-06-13 原文 →
AI 资讯

I built a document converter that never uploads your files

Every time you use an online PDF converter, your file travels to a server somewhere — processed by a company you don't know, stored temporarily on hardware you don't control. For a random meme? Fine. For a payslip, a contract, or a medical document? That's a real privacy problem. What I built ConvertiZen is a document converter that runs 100% in the browser using WebAssembly and modern JS APIs. Your file never leaves your device. Ever. Verify it yourself: open DevTools > Network tab > run a conversion. Zero outgoing requests containing your file data. What it supports 40+ format pairs: PDF ↔ Word, JPG, PNG, Text, HTML, Markdown Excel ↔ CSV, JSON, HTML Images: JPG ↔ PNG ↔ WebP, GIF/BMP → PDF JSON ↔ CSV, Markdown ↔ HTML, XML → JSON How it works PDF.js for PDF parsing docx library for Word generation SheetJS for Excel/CSV Canvas API for image conversions Everything client-side — no server required Pricing 3 free conversions/day, no account needed. Beyond that: €0.69/conversion, €4.99 for a pack of 10, or €4.99/month for unlimited Premium. Feedback welcome What formats are missing? What would make you trust a browser-based tool over a server-based one? 👉 https://convertizen.netlify.app

2026-06-13 原文 →
AI 资讯

How to Convert Word to PDF in the Browser with Vue 3, mammoth, and html2pdf.js

Converting Word documents to PDFs on the server is the classic approach: upload the file, run LibreOffice or a cloud API, send the result back. But that means your users’ resumes, contracts, and reports touch your infrastructure. I wanted something simpler for en.sotool.top : pick a .docx file in the browser, preview the parsed content, and download a PDF. No server involved. Here is how I built it with Vue 3, mammoth , and html2pdf.js . Why Client-Side? The main reason is privacy. Resumes, contracts, tax documents — users do not want them on a stranger’s server. Client-side conversion also means: No upload bandwidth limits No file size caps from your server No storage to clean up Works offline after the page loads The trade-off is that very complex documents are limited by the browser’s rendering capabilities. For typical office documents, that is fine. The Stack Vue 3 — UI, file handling, and reactive state mammoth — Parse .docx files into clean HTML html2pdf.js — Render the HTML into a PDF using html2canvas + jsPDF Native File API — File selection npm install mammoth html2pdf.js Loading the Word Document The first step is reading the uploaded .docx file into an ArrayBuffer , then converting it to HTML with mammoth . import mammoth from ' mammoth ' ; async function convertDocxToHtml ( file ) { const arrayBuffer = await file . arrayBuffer (); const result = await mammoth . convertToHtml ({ arrayBuffer }); return result . value ; } mammoth intentionally produces simple, clean HTML. It ignores complex formatting like text boxes and embedded fonts, which makes the output predictable. I keep the HTML in a reactive ref and render it in a preview panel: < template > <div ref= "previewRef" class= "word-preview" v-html= "htmlContent" ></div> </ template > < script setup > import { ref } from ' vue ' ; const htmlContent = ref ( '' ); const previewRef = ref ( null ); </ script > Generating the PDF Once the user is happy with the preview, html2pdf.js turns the preview element

2026-06-13 原文 →
AI 资讯

What Nobody Told Me About Maintaining an Open Source Project

I am a solo learner. I started coding last year with the help of AI and sometimes without any tutorials or courses. At first, I thought this journey would be easier. But soon I realized something important — no AI or tool can fully solve the real problems I was facing as a developer. I used AI a lot. It explained things with confidence and even provided code. But when I ran that code in my terminal, many times it didn’t work. That’s when I understood something important: AI can guide, but it cannot replace understanding. After facing these issues, I changed my way of learning. Instead of blindly trusting AI, I started: Finding real open-source projects Studying how they were built Listing important topics from those projects Reading documentation carefully Asking AI to explain specific lines of code This helped me understand real-world code better. From this learning journey, I realized something: I should also build my own open-source projects. At first, I believed that creating a powerful project could automatically bring attention and users. But I was wrong. I made a mistake — I was not active on any platform. I was just coding inside VS Code, without communication or sharing my work anywhere. Then I realized: Being a developer is not only about coding. Visibility and communication are also important. After that realization, I started being active on platforms like Dev.to, LinkedIn, and other developer communities. I started posting my work and sharing my progress. Even though I didn’t get many comments, I started getting reactions and engagement. That small feedback gave me motivation. From this journey, I learned something important: Open source is not only about code. It is about helping other developers, sharing knowledge, and being consistent and visible. A developer should not only code silently but also participate in the community. Now I understand that coding is only one part of being a developer. Community, communication, and consistency are equally imp

2026-06-13 原文 →
AI 资讯

Why Retry Is One Of The Most Dangerous Keywords In Software

Few lines of code look more innocent than this: retry ( 3 ) It feels responsible. Professional. Resilient. After all, networks fail. Servers become unavailable. Databases occasionally time out. Retrying seems like the obvious solution. And sometimes it is. But after enough years building production systems, I've become convinced of something: Retry is one of the most dangerous keywords in software. Not because retries are bad. Because retries amplify everything. Good systems become more reliable. Bad systems become disasters. The problem is that many developers treat retries as a reliability feature when they're actually a distributed systems feature. And distributed systems are where simple ideas go to become complicated. Why Retries Exist Imagine: await fetch ( " /api/users " ); The request fails. Maybe: Network hiccup Temporary database issue Load balancer restart Service deployment The operation might succeed if attempted again. So we write: retry ( 3 ) Seems reasonable. And in many cases: It Works Which is why retries become popular. The Dangerous Assumption Most developers unconsciously assume: Failure = Operation Did Not Execute Unfortunately that's not always true. A request can: Execute Successfully ↓ Response Never Arrives From the client's perspective: Failure From the server's perspective: Success Now a retry becomes dangerous. The Double Payment Problem Imagine a payment service. await chargeCard ( order ); The card processor successfully charges: $100 The response is lost due to a network issue. Client sees: Request Failed and retries. await chargeCard ( order ); again. Now: Charge #1 = Success Charge #2 = Success The customer paid twice. Nobody wrote bad logic. The retry created the bug. The Email Storm Problem Consider: await sendWelcomeEmail ( user ); Email provider accepts the message. Response times out. Application retries. await sendWelcomeEmail ( user ); again. Customer receives: Welcome! Welcome! Welcome! Welcome! Support ticket created. Marke

2026-06-13 原文 →
AI 资讯

No Suggest - distraction-free YouTube client

I have been frustrated with YouTube for a while. Not the content, but the everything around it. The homepage full of bait, the auto-play into things I didn't ask for, the Shorts that hijack your scroll, the recommendations that somehow know exactly what will keep you there longest. So I built NoSuggest. What it is A YouTube feed reader that shows you only the channels you follow, nothing else. No algorithm, no recommendations, no Shorts, no homepage, no auto-play, no endless side cards of videos. You add a channel, it fetches their latest videos, done. It lives at nosuggest.com and installs as a PWA on any device — iPhone, Android, desktop — straight from the browser. No app store. The interesting technical constraint: one HTML file The entire app is a single index.html. No account setup, no sign-in, no data collection. Everything that needs to persist — your channel list, saved videos, settings — lives in localStorage. No search history. No watch history. No "you might also like." No trending section. No notification badges designed to create anxiety. No dark patterns anywhere. Every time I was tempted to add something convenient, I asked: does this serve the user's intention, or does it serve engagement? If it was the latter, it didn't make the cut. Try it nosuggest.com — Source Available here , free forever. Curious what others think about this as useful. Thank you.

2026-06-13 原文 →
AI 资讯

Switch an old script from Rhino to the V8 runtime

Originally written for bulldo.gs — republished here with the canonical link pointing home. I want to enable the V8 runtime on an existing Apps Script project so I can use modern JavaScript, but I am worried about breaking things that are already working. // appsscript.json — set runtimeVersion to enable V8 // Rollback: change V8 back to DEPRECATED_ES5 { " timeZone " : " America/New_York " , " dependencies " : {}, " exceptionLogging " : " STACKDRIVER " , " runtimeVersion " : " V8 " } // Code.gs — safe V8-compatible replacement for a common Rhino pattern // Rhino allowed: for each (var item in collection) {} // V8 requires standard for...of instead function listSheetNames () { var ss = SpreadsheetApp . getActiveSpreadsheet (); var names = []; var sheets = ss . getSheets (); for ( var i = 0 ; i < sheets . length ; i ++ ) { names . push ( sheets [ i ]. getName ()); } Logger . log ( names . join ( ' , ' )); } The one-line change and why it is a project-wide bomb Open the Apps Script editor, click Project Settings (the gear icon), and check "Show appsscript.json manifest file in editor." Then open that file and change "runtimeVersion": "DEPRECATED_ES5" to "runtimeVersion": "V8" . Save. That is the entire migration from a settings standpoint. What catches people off guard is the failure mode. V8 parses every .gs file in the project as a unit before running anything. One Rhino-only statement — a for each loop, a __iterator__ method, a Date.prototype.getYear call in an otherwise untouched utility file — causes a syntax or runtime error that prevents the whole script from initializing. Not just the file that contains the bad line. Every function, every trigger, the entire project goes dark. The first time I hit this it took me twenty minutes to figure out why a completely unrelated trigger had stopped firing. The error message pointed at the Rhino syntax in a helper file I had not touched in two years. V8 does not isolate the damage; it fails at parse time, before any executi

2026-06-13 原文 →
AI 资讯

Fix "Exceeded maximum execution time" in Apps Script

Originally written for bulldo.gs — republished here with the canonical link pointing home. I'm running a script that processes a large spreadsheet and it keeps dying with "Exceeded maximum execution time" before it finishes. // Checkpoint-resume pattern for long-running sheet jobs function processInBatches () { var props = PropertiesService . getScriptProperties (); var startRow = parseInt ( props . getProperty ( ' lastRow ' ) || ' 2 ' , 10 ); var sheet = SpreadsheetApp . getActiveSpreadsheet (). getActiveSheet (); var lastDataRow = sheet . getLastRow (); var BATCH = 200 ; var SAFE_MS = 5 * 60 * 1000 ; var started = Date . now (); var endRow = Math . min ( startRow + BATCH - 1 , lastDataRow ); var data = sheet . getRange ( startRow , 1 , endRow - startRow + 1 , 5 ). getValues (); for ( var i = 0 ; i < data . length ; i ++ ) { if ( Date . now () - started > SAFE_MS ) { props . setProperty ( ' lastRow ' , String ( startRow + i )); return ; } // process data[i] here } if ( endRow >= lastDataRow ) { props . deleteProperty ( ' lastRow ' ); deleteTrigger_ (); } else { props . setProperty ( ' lastRow ' , String ( endRow + 1 )); } } The 6-minute wall is per-execution, not per-task Apps Script enforces a hard 6-minute execution time limit per run, regardless of whether you're on a free account or a Workspace account (which bumps the limit to 30 minutes, but the same cliff exists). The error doesn't mean your logic is wrong; it means one continuous call to your function took too long. The fix is to stop thinking of your job as a single execution and start thinking of it as a pipeline of short runs. The first time I hit this, I wasted an afternoon trying to speed up the loop. Marginal gains didn't move the needle because the data volume was the real problem — 4,000 rows at one Sheets API call per row will always breach 6 minutes. The correct frame is: how do I save where I stopped and pick up there next run? Saving and restoring a cursor with PropertiesService PropertiesServic

2026-06-13 原文 →
开发者

Merge multiple docs into one in Google Docs

Originally written for bulldo.gs — republished here with the canonical link pointing home. I want to programmatically combine several Google Docs into one file without losing tables or list formatting. // Merges all source docs into destDocId, in order. // Run from the Apps Script editor; no triggers needed. function mergeDocs () { var sourceIds = [ ' DOC_ID_ONE ' , ' DOC_ID_TWO ' , ' DOC_ID_THREE ' ]; var dest = DocumentApp . openById ( ' DEST_DOC_ID ' ). getBody (); for ( var i = 0 ; i < sourceIds . length ; i ++ ) { var srcBody = DocumentApp . openById ( sourceIds [ i ]). getBody (); var total = srcBody . getNumChildren (); for ( var j = 0 ; j < total ; j ++ ) { var el = srcBody . getChild ( j ); var type = el . getType (); if ( type === DocumentApp . ElementType . PARAGRAPH ) { dest . appendParagraph ( el . asParagraph (). copy ()); } else if ( type === DocumentApp . ElementType . TABLE ) { dest . appendTable ( el . asTable (). copy ()); } else if ( type === DocumentApp . ElementType . LIST_ITEM ) { dest . appendListItem ( el . asListItem (). copy ()); } } } } Why there is no single appendElement call The Document service in Apps Script does not expose a generic appendElement method on Body . Every element type has its own typed append method: appendParagraph , appendTable , appendListItem , and so on. That means a merge loop that ignores element types will throw TypeError: el.copy is not a function the moment it hits a table, because you would be passing an Element where the API expects a Table . The fix is to call getType() on each child element and switch on DocumentApp.ElementType . The type enum values are strings like PARAGRAPH , TABLE , LIST_ITEM , INLINE_IMAGE , and HORIZONTAL_RULE . In practice the first three account for almost all real document content. The code above handles those three and silently skips anything else (images, rules) rather than crashing the entire merge. Getting the doc IDs and running the script The ID for any Google Doc is the lo

2026-06-13 原文 →
AI 资讯

Import JSON from an API in Google Sheets

Originally written for bulldo.gs — republished here with the canonical link pointing home. I want to pull live JSON data from an API endpoint directly into a Google Sheet without installing an add-on. // Fetch JSON from an API and write it to the active sheet // Adjust API_URL and the field list to match your endpoint function importJsonFromApi () { var API_URL = ' https://jsonplaceholder.typicode.com/users ' ; var sheet = SpreadsheetApp . getActiveSpreadsheet (). getActiveSheet (); var response = UrlFetchApp . fetch ( API_URL ); var raw = response . getContentText (); var data = JSON . parse ( raw ); var headers = [ ' id ' , ' name ' , ' username ' , ' email ' , ' phone ' ]; var rows = data . map ( function ( obj ) { return headers . map ( function ( key ) { return obj [ key ] || '' ; }); }); sheet . clearContents (); sheet . getRange ( 1 , 1 , 1 , headers . length ). setValues ([ headers ]); sheet . getRange ( 2 , 1 , rows . length , headers . length ). setValues ( rows ); } Why getContentText() comes before JSON.parse UrlFetchApp.fetch() returns an HTTPResponse object, not a string. The first time I skipped getContentText() and passed the response object directly to JSON.parse(), it silently parsed to null and the sheet wrote nothing. You need raw = response.getContentText() to get the actual body as a string, then JSON.parse(raw) turns it into a JavaScript object or array. The URL must be publicly accessible or accept an API key via a query parameter or Authorization header. Add headers like this: UrlFetchApp.fetch(url, { headers: { Authorization: 'Bearer ' + token } }). Apps Script's UrlFetchApp quota is 20,000 calls per day on a free Google account, 100,000 on Workspace. The rectangular array constraint — why setValues fails without mapping setValues() is strict: it requires a 2D array where every row has the same number of columns. If you hand it an array of plain JSON objects, it throws 'The number of rows or columns in the range does not match the number of

2026-06-13 原文 →
AI 资讯

Send personalized emails from a sheet in Gmail

Originally written for bulldo.gs — republished here with the canonical link pointing home. I have a spreadsheet of names and email addresses and I want to send each person a personalized message from my Gmail account without copy-pasting or using a paid tool. // Mail merge: Sheet cols A=Name, B=Email, C=Sent // Run from Apps Script; authorize Gmail + Sheets scopes function sendMerge () { var sheet = SpreadsheetApp . getActiveSheet (); var rows = sheet . getDataRange (). getValues (); var quota = MailApp . getRemainingDailyQuota (); var sent = 0 ; for ( var i = 1 ; i < rows . length ; i ++ ) { if ( rows [ i ][ 2 ] === ' Sent ' ) continue ; if ( sent >= quota ) { Logger . log ( ' Quota reached at row ' + ( i + 1 )); break ; } var name = rows [ i ][ 0 ]; var email = rows [ i ][ 1 ]; var subject = ' Hey ' + name + ' , here is your update ' ; var body = ' Hi ' + name + ' , \n\n Your personalized content goes here. \n\n Thanks ' ; MailApp . sendEmail ( email , subject , body ); sheet . getRange ( i + 1 , 3 ). setValue ( ' Sent ' ); sent ++ ; } } Set up your sheet and open the script editor Put names in column A, email addresses in column B, and leave column C blank — the script writes 'Sent' there as it goes. Header row in row 1 is assumed; the loop starts at index 1 (row 2) to skip it. Open the script editor from Extensions > Apps Script, paste the function, and save. The first time you run sendMerge() Google will ask you to authorize two scopes: Sheets (read/write the active spreadsheet) and Gmail (send mail on your behalf). Both are required. If you only see a Sheets prompt, delete the file and re-paste — a cached partial authorization sometimes skips the Gmail scope on older script files. Why the Sent column is the whole point Consumer Google accounts cap at roughly 100 outgoing recipients per 24-hour rolling window via MailApp. If your list has 200 rows and you run the script at 11 pm, it will send 100 and log 'Quota reached at row 101'. Without the Sent check, a sec

2026-06-13 原文 →
开发者

Remove duplicate rows in Google Sheets

Originally written for bulldo.gs — republished here with the canonical link pointing home. I have a Google Sheet with duplicate rows and I want to remove them programmatically, either on demand or on a schedule, without destroying the rest of my data. // removeDuplicates.gs — dedup active sheet, keep first occurrence // Run from Extensions > Apps Script, or bind to a trigger. function removeDuplicateRows () { var sheet = SpreadsheetApp . getActiveSheet (); var data = sheet . getDataRange (). getValues (); var seen = new Set (); var unique = []; for ( var i = 0 ; i < data . length ; i ++ ) { var key = data [ i ]. join ( ' | ' ); if ( ! seen . has ( key )) { seen . add ( key ); unique . push ( data [ i ]); } } sheet . clearContents (); sheet . getRange ( 1 , 1 , unique . length , unique [ 0 ]. length ). setValues ( unique ); } Why rewrite instead of delete The instinct when deduplicating is to loop through the sheet and call deleteRow on each duplicate. That works, but it has a sharp edge: every call to deleteRow shifts all rows below it up by one. If you delete row 3, what was row 4 is now row 3, and your loop index is already pointing at the new row 4. The safe workaround people reach for is iterating bottom-to-top, which works but means holding the full duplicate set in memory anyway, making one API call per deleted row. The approach here sidesteps the problem entirely. Read everything once with getDataRange().getValues() — a single API call that returns a 2D array. Build the deduplicated array in JavaScript using a Set to track which row fingerprints you have already seen. Then clear the sheet and write the result back with one setValues call. Two API calls total, regardless of how many duplicates you had. For a 10,000-row sheet, this is the difference between a script that finishes in two seconds and one that times out at the six-minute Apps Script execution limit. The row key is built with data[i].join('|'). The pipe character works as a separator in practice; i

2026-06-13 原文 →
AI 资讯

Frameworks Rot. The Platform Doesn't.

A decision memo for anyone staring at their package.json and wondering. Most arguments for leaving your SPA framework center on the upgrade treadmill — the endless cycle of major-version migrations, dependency churn, and build-tool turnover. That argument is real but incomplete, and on its own it has never been decisive: every framework shop has learned to live with the treadmill. There's a stronger case, built on four pillars that compound with each other. First, total cost of ownership : vanilla JavaScript on the web platform has unusual TCO properties, dominated by a depreciation curve that is nearly flat. Code written against the platform does not rot, because its substrate does not change. Over long horizons, this single property outweighs almost every per-feature productivity argument in a framework's favor. Second, the labor market : the pool of people who can work on vanilla JavaScript is not a niche within the frontend market — it is the entire frontend market, plus most of the backend market. Every framework developer is, underneath, a JavaScript developer. The reverse is not true. If you hire for a specific framework, you're hiring from a subset while telling yourself you're hiring from the mainstream. Third, AI leverage : engineers now produce a growing share of code with AI assistance, and the economics of that assistance differ sharply by target. The web platform is a small, stable, exhaustively documented body of knowledge; a framework ecosystem is a large, fast-mutating one whose training data is perpetually stale. AI coding tools are measurably more reliable on the former. As AI-assisted development becomes the dominant mode of production, the substrate that AI handles best becomes the cheaper substrate — and the gap widens every year the platform stays still while frameworks move. Fourth, architecture : porting to Web Components is not a transliteration of the same design into different syntax. The platform pushes toward a genuinely different archi

2026-06-13 原文 →
AI 资讯

HTML-First Websites Are Quietly Winning Again in 2026

TL;DR: HTML-first means shipping real, server-rendered content before any JavaScript runs, then adding scripts only where they earn their place. In 2026 this approach is winning again, not out of nostalgia, but because the median mobile page now ships around 646 KB of JavaScript, fewer than half of mobile sites pass Core Web Vitals, and the browser already does natively what many sites still pull in libraries for. For most business websites, progressive enhancement is faster to ship, cheaper to run, and easier to keep alive. Sometime in 2026, "just use HTML" stopped being a contrarian take. I noticed it first in my own client work, not in a conference talk. The sites that start close to the platform, plain HTML, forms, links, server rendering, and add JavaScript only where it genuinely helps, are the ones that launch faster, load cleaner, and generate fewer confused support messages two months later. This is not anti-JavaScript. It is a reaction to a decade of reaching for a framework before asking whether the project needed one. The pendulum is swinging back toward the browser, and the numbers explain why. What HTML-first actually means (and why it is not 2009 web design) The fastest way to misunderstand this is to picture table layouts and inline styles. That is not it. HTML-first is an order of operations. You build a page that is complete and usable as server-rendered HTML, then you enhance it. The content is readable before a single script loads. The form submits even if JavaScript never arrives. This is the old idea of progressive enhancement , applied deliberately with modern tools instead of by accident. There is a small but real movement around this now. The HTML First community manifesto argues, fairly, that the platform has far more capability than most teams use. You do not have to agree with every line of it to notice the shift. The point is not to ban JavaScript. The point is to stop treating it as the default starting material for every page. The 2026

2026-06-13 原文 →