第三方金融计算器加载存在明显竞态条件问题排查
解决第三方Finance Calculator集成的竞态条件报错问题
我正在将第三方Finance Calculator集成到官网中,其中decimal.min.js、pbf-calculator.js、pbf-calculator.css为第三方资源,pbf-calculator-init.js是我编写的初始化脚本。
页面偶尔能正常加载无报错,但有时控制台会抛出:
Uncaught TypeError: Cannot read properties of null (reading 'getAttribute')
错误出现在pbf-calculator.js第1411行,该行代码为:
m = d.getAttribute("maxLoan") ? parseFloat(d.getAttribute("maxLoan")) : e.settings.maxLoan ? parseFloat(e.settings.maxLoan) : parseFloat(e.settings.limits.maxLoan),
当前HTML模板(form.tpl):
<div id="pbf_calculator_container"></div> <script src="/assets/js/decimal.min.js"></script> <script type="text/javascript" src="/assets/js/pbf-calculator.js"></script> <link rel="stylesheet" href="/assets/css/pbf-calculator.css" type="text/css" media="screen" /> <script type="text/javascript" src="/assets/js/pbf-calculator-init.js"></script>
初始化脚本pbf-calculator-init.js核心代码:
financeCalculator({ target: "div#pbf_calculator_container", config: "/assets/js/pbf-calculator-config.json", loan_amount_target: ".order-total strong", rep_example: { cash_price: 8000, deposit: 3000, loan_amount: 5000, apr: 9.9, interest_rate: 9.9, term: 12, instalment_amount: 438.38, total_term: 12, credit_charge: 260.56, total_amount_payable: 8260.56 }, colours: { primary: "#7D3CFF", darker: null }, labels: { amount: "Enter the amount you would like to spend", deposit: "Enter the amount you would like to deposit", service: "Select a finance service", term: "Select a repayment term", results: "Finance Summary", deferralterm: "Select a deferral period" }, classes: { buttons: "pbf_button", button_wrapper: "pbf_button_wrapper", button_list: "pbf_button_list", inputs: "pbf_input", input_wrapper: "pbf_input_wrapper", labels: "pbf_label", results_box: "pbf_results" } });
问题核心是竞态条件:脚本执行时,代码尝试获取属性的DOM元素d还未完成加载或创建,导致d为null,进而调用getAttribute触发报错。
解决方案
方案1:确保DOM完全加载后再初始化计算器
将初始化代码包裹在DOMContentLoaded事件监听中,保证页面DOM解析完成后再执行初始化逻辑:
修改pbf-calculator-init.js:
document.addEventListener('DOMContentLoaded', function() { financeCalculator({ target: "div#pbf_calculator_container", config: "/assets/js/pbf-calculator-config.json", loan_amount_target: ".order-total strong", rep_example: { cash_price: 8000, deposit: 3000, loan_amount: 5000, apr: 9.9, interest_rate: 9.9, term: 12, instalment_amount: 438.38, total_term: 12, credit_charge: 260.56, total_amount_payable: 8260.56 }, colours: { primary: "#7D3CFF", darker: null }, labels: { amount: "Enter the amount you would like to spend", deposit: "Enter the amount you would like to deposit", service: "Select a finance service", term: "Select a repayment term", results: "Finance Summary", deferralterm: "Select a deferral period" }, classes: { buttons: "pbf_button", button_wrapper: "pbf_button_wrapper", button_list: "pbf_button_list", inputs: "pbf_input", input_wrapper: "pbf_input_wrapper", labels: "pbf_label", results_box: "pbf_results" } }); });
方案2:动态加载第三方脚本并确保加载完成后初始化
如果第三方脚本pbf-calculator.js存在加载延迟,可通过动态加载脚本的方式,在资源加载完成后再执行初始化:
修改pbf-calculator-init.js:
// 动态加载decimal.min.js const decimalScript = document.createElement('script'); decimalScript.src = '/assets/js/decimal.min.js'; decimalScript.onload = loadCalculatorScript; document.body.appendChild(decimalScript); function loadCalculatorScript() { // 加载pbf-calculator.js const calculatorScript = document.createElement('script'); calculatorScript.src = '/assets/js/pbf-calculator.js'; calculatorScript.onload = initCalculator; document.body.appendChild(calculatorScript); // 加载样式文件 const calculatorStyle = document.createElement('link'); calculatorStyle.rel = 'stylesheet'; calculatorStyle.href = '/assets/css/pbf-calculator.css'; calculatorStyle.media = 'screen'; document.head.appendChild(calculatorStyle); } function initCalculator() { financeCalculator({ target: "div#pbf_calculator_container", config: "/assets/js/pbf-calculator-config.json", loan_amount_target: ".order-total strong", // 其他配置项同之前 }); }
同时修改HTML模板,仅保留容器元素:
<div id="pbf_calculator_container"></div>
方案3:修改第三方脚本的空值检查(若有权限修改)
直接在pbf-calculator.js第1411行添加d的空值判断,避免null调用getAttribute:
m = d && d.getAttribute("maxLoan") ? parseFloat(d.getAttribute("maxLoan")) : e.settings.maxLoan ? parseFloat(e.settings.maxLoan) : parseFloat(e.settings.limits.maxLoan),
内容的提问来源于stack exchange,提问作者Zabs
相关产品推荐
相关产品推荐

