赣州市章贡区建设路居委会网站/网站seo运营
env.js(env的初始化以及服务的加载)
路径:addons\web\static\src\env.js
这个文件的作用就是初始化env,主要是加载所有的服务。如orm, title, dialog等。
1、env.js 的加载时机
前文我们讲过前端的启动函数,start.js,其中有这么两句,这里有两个函数makeEnv和startServices,都在同级目录的env.js里
const env = makeEnv();await startServices(env);
2、 makeEnv()
export function makeEnv() {return {bus: new EventBus(),services: {},debug: odoo.debug,get isSmall() {throw new Error("UI service not initialized!");},};
}
从代码可以看出,env有4个属性:
- bus: 全局数据总线
- services:全局服务对象
- debug: 是否debug模式
- isSmall:判断手机端还是移动端
这里抛出一个异常是为了在后面会覆盖这个方法。
从中可以看出,env最重要的其实就两个对象,一个是全局数据总线,负责组件之间通信,另一个就是service。
3、startServices
启动所有在注册表中注册的服务,并且保证所有的依赖都得到满足。
首先获取所有在注册表中注册的服务serviceRegistry
初始化元数据SERVICES_METADATA, 主要是保存service中sync的内容,有什么作用还不清楚
主角开始上场: startServices
启动所有在注册表中注册的服务,并且保证每个服务的依赖都得到满足。
await Promise.resolve(); // 等待之前的异步操作都完成const toStart = new Set(); // 初始化要启动的服务,因为每个服务都不同,所以用了setserviceRegistry.addEventListener // 这段没看懂,这里添加了一个事件监听,动态增加注册服务的await _startServices(env, toStart); // 将env和toStart对象传入,干正事了
4._startServices(env, toStart)
4.1 填充 toStart 集合
const services = env.services; //获取了env中的service对象,这时候还是空的,啥也没有// 根据注册表中的服务信息,初始化了toStart集合,里面都是创建好的实例对象,并且有一个name属性。for (const [name, service] of serviceRegistry.getEntries()) {if (!(name in services)) {const namedService = Object.assign(Object.create(service), { name });toStart.add(namedService);}}
4.2、findNext()
关键的地方来了, 这里有一个关键的函数findNext(),先看看它的定义:
// 从toStart集合中遍历,有过集合中的服务有依赖并且依赖都已经满足了,则返回这个service,如果没有依赖,那么直接返回,找到第一个就返回,不叨叨,直到找不到,返回null
function {for (const s of toStart) {if (s.dependencies) {if (s.dependencies.every((d) => d in services)) {return s;}} else {return s;}}return null;}
4.3 、启动服务,并填充到env的services中
在start函数中,findNext函数返回的服务,第一步要最的就是从toStart 删除,这样遍历的次数会越来越少,我看过,odoo17一共有69个服务,while一共还要遍历69次,每次加载一个服务。 通过这种方式,很好的处理了服务之间的依赖关系,并且最大限度的实现了并行。
// start as many services in parallel as possible 并行启动尽可能多的服务async function start() {let service = null;const proms = [];while ((service = findNext())) {const name = service.name;toStart.delete(service); // 删除要加载的服务const entries = (service.dependencies || []).map((dep) => [dep, services[dep]]);const dependencies = Object.fromEntries(entries);let value;try {value = service.start(env, dependencies); // 调用start函数,并将返回值付给value} catch (e) {value = e;console.error(e);}if ("async" in service) {SERVICES_METADATA[name] = service.async; // 保存服务的元数据,后面可能会有用}if (value instanceof Promise) { // 如果value是一个Promiseproms.push(new Promise((resolve) => {value.then((val) => {services[name] = val || null; // 将promise的返回值保存到services中}).catch((error) => {services[name] = error;console.error("Can't load service '" + name + "' because:", error);}).finally(resolve);}));} else {services[service.name] = value || null; // 如果不是promise,直接将value保存到services中}}await Promise.all(proms); // 等待所有的proms完成if (proms.length) {return start(); }}
到这里,前端js就完成了所有service的加载,要注意的是, env.services中保存的不是 services对象本身,而是service对象的start函数返回的对象。
这点很重要,每个service都要有start函数,而且要有返回值。
4.4、后面还有一段是异常处理
if (toStart.size) {const names = [...toStart].map((s) => s.name);const missingDeps = new Set();[...toStart].forEach((s) =>s.dependencies.forEach((dep) => {if (!(dep in services) && !names.includes(dep)) {missingDeps.add(dep);}}));const depNames = [...missingDeps].join(", ");throw new Error(`Some services could not be started: ${names}. Missing dependencies: ${depNames}`);}
如果toStart.size >0 ,说明这里面的服务的依赖想没有得到满足,所以无法加载,会抛出一个Error
5、附录:odoo17 env.js
/** @odoo-module **/import { registry } from "./core/registry";import { EventBus } from "@odoo/owl";// -----------------------------------------------------------------------------
// Types
// -----------------------------------------------------------------------------/*** @typedef {Object} OdooEnv* @property {import("services").Services} services* @property {EventBus} bus* @property {string} debug* @property {(str: string) => string} _t* @property {boolean} [isSmall]*/// -----------------------------------------------------------------------------
// makeEnv
// -----------------------------------------------------------------------------/*** Return a value Odoo Env object** @returns {OdooEnv}*/
export function makeEnv() {return {bus: new EventBus(),services: {},debug: odoo.debug,get isSmall() {throw new Error("UI service not initialized!");},};
}// -----------------------------------------------------------------------------
// Service Launcher
// -----------------------------------------------------------------------------const serviceRegistry = registry.category("services");export const SERVICES_METADATA = {};
let startServicesPromise = null;/*** Start all services registered in the service registry, while making sure* each service dependencies are properly fulfilled.** @param {OdooEnv} env* @returns {Promise<void>}*/
export async function startServices(env) {// Wait for all synchronous code so that if new services that depend on// one another are added to the registry, they're all present before we// start them regardless of the order they're added to the registry.debuggerawait Promise.resolve();const toStart = new Set();serviceRegistry.addEventListener("UPDATE", async (ev) => {// Wait for all synchronous code so that if new services that depend on// one another are added to the registry, they're all present before we// start them regardless of the order they're added to the registry.await Promise.resolve();const { operation, key: name, value: service } = ev.detail;if (operation === "delete") {// We hardly see why it would be usefull to remove a service.// Furthermore we could encounter problems with dependencies.// Keep it simple!return;}if (toStart.size) {const namedService = Object.assign(Object.create(service), { name });toStart.add(namedService);} else {await _startServices(env, toStart);}});await _startServices(env, toStart);
}async function _startServices(env, toStart) {if (startServicesPromise) {return startServicesPromise.then(() => _startServices(env, toStart));}const services = env.services;for (const [name, service] of serviceRegistry.getEntries()) {if (!(name in services)) {const namedService = Object.assign(Object.create(service), { name });toStart.add(namedService);}}// start as many services in parallel as possibleasync function start() {let service = null;const proms = [];while ((service = findNext())) {const name = service.name;toStart.delete(service);const entries = (service.dependencies || []).map((dep) => [dep, services[dep]]);const dependencies = Object.fromEntries(entries);let value;try {value = service.start(env, dependencies);} catch (e) {value = e;console.error(e);}if ("async" in service) {SERVICES_METADATA[name] = service.async;}if (value instanceof Promise) {proms.push(new Promise((resolve) => {value.then((val) => {services[name] = val || null;}).catch((error) => {services[name] = error;console.error("Can't load service '" + name + "' because:", error);}).finally(resolve);}));} else {services[service.name] = value || null;}}await Promise.all(proms);if (proms.length) {return start();}}startServicesPromise = start();await startServicesPromise;startServicesPromise = null;if (toStart.size) {const names = [...toStart].map((s) => s.name);const missingDeps = new Set();[...toStart].forEach((s) =>s.dependencies.forEach((dep) => {if (!(dep in services) && !names.includes(dep)) {missingDeps.add(dep);}}));const depNames = [...missingDeps].join(", ");throw new Error(`Some services could not be started: ${names}. Missing dependencies: ${depNames}`);}function findNext() {for (const s of toStart) {if (s.dependencies) {if (s.dependencies.every((d) => d in services)) {return s;}} else {return s;}}return null;}
}
相关文章:

odoo16前端框架源码阅读——env.js
env.js(env的初始化以及服务的加载) 路径:addons\web\static\src\env.js 这个文件的作用就是初始化env,主要是加载所有的服务。如orm, title, dialog等。 1、env.js 的加载时机 前文我们讲过前端的启动函数,start.…...

浙大恩特客户资源管理系统 SQL注入漏洞复现
0x01 产品简介 浙大恩特客户资源管理系统是一款针对企业客户资源管理的软件产品。该系统旨在帮助企业高效地管理和利用客户资源,提升销售和市场营销的效果。 0x02 漏洞概述 浙大恩特客户资源管理系统中T0140_editAction.entweb接口处存在SQL注入漏洞,未…...

ESP32网络开发实例-BME280传感器数据保存到InfluxDB时序数据库
BME280传感器数据保存到InfluxDB时序数据库 文章目录 BME280传感器数据保存到InfluxDB时序数据库1、BM280和InfluxDB介绍2、软件准备3、硬件准备4、代码实现在本文中,将详细介绍如何将BME280传感器数据上传到InfluxDB中,方便后期数据处理。 1、BM280和InfluxDB介绍 InfluxDB…...

C++中sort()函数的greater<int>()参数
目录 1 基础知识2 模板3 工程化 1 基础知识 sort()函数中的greater<int>()参数表示将容器内的元素降序排列。不填此参数,默认表示升序排列。 vector<int> a {1,2,3}; sort(a.begin(), a.end(), greater<int>()); //将a降序排列 sort(a.begin()…...

2024有哪些免费的mac苹果电脑内存清理工具?
在我们日常使用苹果电脑的过程中,随着时间的推移,可能会发现设备的速度变慢了,甚至出现卡顿的现象。其中一个常见的原因就是程序占用内存过多,导致系统无法高效地运行。那么,苹果电脑内存怎么清理呢?本文将…...

线性表的概念
目录 1.什么叫线性表2.区分线性表的题 1.什么叫线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是…...

锐捷练习-ospf虚链路及rip路由相互引入
一、相关知识补充 1、ospf基本概述 OSPF(Open Shortest Path First)是一种链路状态路由协议,用于在计算机网络中进行路由选择。它是内部网关协议(IGP)之一,常用于大规模企业网络或互联网服务提供商的网络…...
【机器学习】线性回归算法:原理、公式推导、损失函数、似然函数、梯度下降
1. 概念简述 线性回归是通过一个或多个自变量与因变量之间进行建模的回归分析,其特点为一个或多个称为回归系数的模型参数的线性组合。如下图所示,样本点为历史数据,回归曲线要能最贴切的模拟样本点的趋势,将误差降到最小。 2. 线…...

Word中NoteExpress不显示的问题
首先确认我们以及安装了word插件 我们打开word却没有。此时我们打开:文件->选项->加载项 我们发现被禁用了 选择【禁用项目】(如果没有,试一试【缓慢且禁用的加载项】),点击转到 选择启用 如果没有禁用且没有出…...

连接池的大体介绍,常用配置及在springboot项目中的应用
连接池 在Java开发中,常见的数据库连接池有哪些?_java常见数据库连接池_举个例子学java的博客-CSDN博客 常见的连接池配置参数 java 连接池参数 - 百度文库 连接池的具体配法 Spring Boot之默认连接池配置策略_spring mysql默认连接池大小-CSDN博客...

Java之SpringCloud Alibaba【九】【Spring Cloud微服务Skywalking】
Java之SpringCloud Alibaba【一】【Nacos一篇文章精通系列】跳转Java之SpringCloud Alibaba【二】【微服务调用组件Feign】跳转Java之SpringCloud Alibaba【三】【微服务Nacos-config配置中心】跳转Java之SpringCloud Alibaba【四】【微服务 Sentinel服务熔断】跳转Java之Sprin…...

wpf devexpress设置行和编辑器
如下教程示范如何计算行布局,特定的表格单元编辑器,和格式化显示值。这个教程基于前一个文章 选择行显示 GridControl为所有字段生成行和绑定数据源,如果AutoGenerateColumns 属性选择AddNew。添加行到GridControl精确显示为特别的几行设置。…...

AdaBoost 算法:理解、实现和掌握 AdaBoost
一、介绍 Boosting 是一种集成建模技术,由 Freund 和 Schapire 于 1997 年首次提出。从那时起,Boosting 就成为解决二元分类问题的流行技术。这些算法通过将大量弱学习器转换为强学习器来提高预测能力 。 Boosting 算法背后的原理是,我们首先…...

基于ssm+vue设备配件检修管理系统
摘要 随着工业设备的日益复杂和多样化,设备配件的检修管理成为保障生产运行和设备寿命的关键环节。本研究基于SSM框架(Spring Spring MVC MyBatis),致力于设计和实现一套全面、高效的设备配件检修管理系统。该系统不仅能够提高设…...

Reids集群
目录 一、集群的概念 1.为什么要搭建集群? 2.Redis搭建集群是否需要考虑状态同步的问题? 二、Redis集群的模式 1.redis集群--主从模式 1.1什么是Redis的主从模式? 1.2.主从模式它们之间的数据是怎么实现一个同步的? 1.3.主…...

自定义指令基础
除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外,Vue 还允许你注册自定义的指令 (Custom Directives) 选项式API_自定义指令 <template><h3>自定义指令</h3><p v-author>文本信息</p> </template> <script> e…...

软考-高级-系统架构设计师教程(清华第2版)【第5章 软件工程基础知识(190~233)-思维导图】
软考-高级-系统架构设计师教程(清华第2版)【第5章 软件工程基础知识(190~233)-思维导图】 课本里章节里所有蓝色字体的思维导图...

Oneid方案
一、前文 用户画像的前提是标识出用户,存在以下场景:不同业务系统对同一个人的标识,匿名用户行为的行为归因;本文提供多种解决方案,提供大家思考。 二、方案矩阵 三、其他 相关连接: 如何通过图算法能力获…...

【超好用的工具库】hutool-all工具库的基本使用
简介(可不看): hutool-all是一个Java工具库,提供了许多实用的工具类和方法,用于简化Java开发过程中的常见任务。它包含了各种模块,涵盖了字符串操作、日期时间处理、加密解密、文件操作、网络通信、图片处…...

趣学python编程 (一、计算机基础知识科普)
未来是高度科技化和智能化的时代。过去不识字的叫“文盲”,如今不懂点计算机知识,则可能是新时代的“文盲”。不论从事什么行业,了解下计算机和编程都是有益的。Python 连续多年占据最受欢迎的编程语言榜首,未来Python有机会成为像…...

亚马逊云科技云存储服务指南
文章作者:Libai 高效的云存储服务对于现代软件开发中的数据管理至关重要。亚马逊云科技云存储服务提供了强大的工具,可以简化工作流程并增强数据管理能力。 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏…...

随机链表的复制
文章目录 🍉前言🍉题目🍉分析🍉思路一:暴力解法🍉思路二:很绝的办法 🍉前言 果然,力扣的简单题不一定简单,但是中等和较难的题一定很麻烦。 这道题相当综合&…...

树莓派4b编译FFmpeg支持硬件编解码
ffmpeg h264_omx解码器充分发挥树莓派gpu性能 准备 树莓派4b ,64位系统 修改树莓派的启动设置文件(/boot/config.txt)进行如下的调整: gpu_mem=256 framebuffer_depth=16安装依赖 常规依赖: sudo apt update sudo apt upgrade sudo apt -y install autoconf automake …...

开启CentOS/Debian自带的TCP BBR加速
BBR 是什么我就不多做介绍了。如果系统自带内核高于4.9 则默认已包含 BBR。 操作方法: 1、使用 root 权限运行下面代码 uname -r //内核版本高于 4.9 就行。2、开启BBR echo "net.core.default_qdiscfq" >> /etc/sysctl.conf echo "net.ip…...

视频推拉流EasyDSS直播点播平台获取指定时间快照的实现方法
视频推拉流直播点播系统EasyDSS平台,可提供流畅的视频直播、点播、视频推拉流、转码、管理、分发、录像、检索、时移回看等功能,可兼容多操作系统,在直播点播领域具有广泛的场景应用。为了便于用户集成、调用与二次开发。 今天我们来介绍下在…...

CSS---关于font文本属性设置样式总结
目录 1、color属性 2、font-size属性 3、font-weight属性 4、font-family属性 5、text-align属性 6、line-height属性 7、text-indent属性 8、letter-spacing属性 9、word-spacing属性 10、word-break属性 11、white-space属性 12、text-transform 12、writing-mo…...

7、使用真机调试鸿蒙项目
此处以华为手机为例,版本为鸿蒙4.0. 一、打开手机调试功能 1、打开开发者模式 打开“设置”—“关于手机”,连续点击“软件版本”可打开开发者模式 2、开启USB调试功能 打开“设置”—“系统更新”—“开发者选项”,下拉找到“USB调试”…...

GPT实战系列-P-Tuning本地化训练ChatGLM2等LLM模型,到底做了什么?(一)
GPT实战系列-如何使用P-Tuning本地化训练ChatGLM2等LLM模型? 文章目录 GPT实战系列-如何使用P-Tuning本地化训练ChatGLM2等LLM模型?P-Tuning微调训练概述1、预训练模型或者是torch模型2、训练器的超参数3、数据预处理工具4、加载数据5、分词处理6、数据预…...

【Python】爬虫代理IP的使用+建立代理IP池
目录 前言 一、代理IP 1. 代理IP的获取 2. 代理IP的验证 3. 代理IP的使用 二、建立代理IP池 1. 代理IP池的建立 2. 动态维护代理IP池 三、完整代码 总结 前言 在进行网络爬虫开发时,我们很容易遭遇反爬虫机制的阻碍。为了规避反爬虫机制,我们…...

JS-项目实战-新增水果库存功能实现
1、fruit.js function $(name) {if (name) {//假设name是 #fruit_tblif (name.startsWith("#")) {name name.substring(1); //fruit_tblreturn document.getElementById(name);} else {return document.getElementsByName(name); //返回的是NodeList类型}} }//当…...