()
| 574 | window.addLog('[热帖排行] 拉取失败:' + (err && err.message ? err.message : '未知错误')); |
| 575 | } |
| 576 | const dialog = document.getElementById('top-reply-dialog'); |
| 577 | if (dialog) { |
| 578 | const listDiv = dialog.querySelector('#top-reply-list'); |
| 579 | if (listDiv) { |
| 580 | const cached = loadPosts(); |
| 581 | if (cached && cached.length > 0) { |
| 582 | listDiv.innerHTML = '<div style="text-align:center;color:#e74c3c;padding:12px;font-size:12px;">拉取失败,显示缓存数据</div>'; |
| 583 | renderPostList(listDiv, cached); |
| 584 | } else { |
| 585 | listDiv.innerHTML = '<div style="text-align:center;color:#e74c3c;padding:50px 0;font-size:13px;">拉取失败,请稍后重试</div>'; |
| 586 | } |
| 587 | } |
| 588 | } |
| 589 | // 同步刷新侧边栏面板(显示缓存) |
| 590 | refreshSidebarContent(); |
| 591 | }) |
| 592 | .finally(function () { |
| 593 | // 无论成功失败都恢复用户原有排序标签,保证停留在原标签页 |
| 594 | restoreSortByCookie(savedSortBy); |
| 595 | }); |
| 596 | } |
| 597 | |
| 598 | /* 锁内统一二次检查 */ |
| 599 | function preCheck() { |
| 600 | if (isRateLimitCooldownActive()) return 'cooldown'; |
| 601 | if (_peerFetchNotified) { _peerFetchNotified = false; return 'peer_done'; } |
| 602 | if (!forceRefresh) { |
| 603 | var ct = 0; |
| 604 | try { ct = parseInt(localStorage.getItem(STORAGE_KEY + '_time'), 10) || 0; } catch (e) {} |
| 605 | if (loadPosts().length > 0 && Date.now() - ct < FETCH_CACHE_MS) return 'fresh'; |
| 606 | } |
| 607 | return null; // 可以拉取 |
| 608 | } |
| 609 | |
| 610 | /* 拿到锁后执行拉取(返回 Promise 以便 navigator.locks 等待完成) */ |
| 611 | function executeWithLock() { |
| 612 | var skip = preCheck(); |
| 613 | if (skip) return Promise.resolve(); |
| 614 | showWaitingMsg('刷新中…'); |
| 615 | bcSend(BC_MSG_FETCH_START, {}); |
| 616 | var hbTimer = setInterval(refreshLockHeartbeat, 15000); |
| 617 | return doFetch().then(function () { |
| 618 | clearInterval(hbTimer); |
| 619 | bcSend(BC_MSG_FETCH_DONE, {}); |
| 620 | }).catch(function () { |
| 621 | clearInterval(hbTimer); |
| 622 | bcSend(BC_MSG_FETCH_DONE, {}); |
| 623 | }); |
| 624 | } |
| 625 | |
| 626 | /* 跨 tab 原子锁:优先使用 navigator.locks API(浏览器原生原子锁); |
| 627 | 降级方案:localStorage 锁(带重试和二次检查) */ |
| 628 | var LOCK_NAME = 'nodeseek_topreply_fetch'; |
| 629 | var lockRetryCount = 0; |
| 630 |
no test coverage detected