试图封装一个Promise.all方法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Promise</title>
	</head>
	<body>

		<script type="text/javascript">
			/**
			 * new Promise 接收一个function作为参数,
			 * function中有两个回调函数作为参数,
			 *   一个成功的回调函数,一个是失败的回调函数
			 * **/
			var p3 = new Promise(function(resolve, reject) {
				setTimeout(() => {
					resolve('3秒钟结束了')
				}, 3000)
			})
			var p5 = new Promise(function(resolve, reject) {
				setTimeout(() => {
					resolve('5秒钟结束了')
				}, 5000)
			})
			var p2 = new Promise(function(resolve, reject) {
				setTimeout(() => {
					resolve('2秒钟结束了')
				}, 2000)
			})
			p3.then(r => console.log(r))
			p5.then(r => console.log(r))
			p2.then(r => console.log(r))

			//Promise的链式调用 then中的回调函数可以接收上一次then的返回值
			p2.then(r => {
				console.log(r)
				return 'aaa'
			}).then(c => console.log(c))


			//Promise.all  它是以数组作为对象
			//  当数组中的所有Promise都执行完毕之后才执行then
			//  返回的数据是每个Promise对象数据组成的数组,结果是按照参数顺序执行的
			// Promise.all([p2,p3,p5]).then(res=>console.log(res))
			function promiseAll(arr) {
				let newArr = []
				return new Promise((resolve,reject)=>{
					arr.forEach((item,index) => {
						item.then(r => {
							newArr.push(r)
							let flag = newArr.every(v=>v!=null)
							if(newArr.length==arr.length && flag){
								resolve(newArr)
							}
						})
					})
				})
			}
			promiseAll([p2, p5, p3]).then(r=>console.log(r))
		</script>
	</body>
</html>

/** * 微信小程序数据管理器 * 基于DataManager.js的逻辑,但适配微信小程序环境 */ // 解析数据引用关系的辅助函数 /** * 解析数据中的引用关系 * * 该函数用于处理嵌套的数据结构,将数据中的引用关系解析为实际的对象引用。 * 它会遍历数据中的所有实体,查找属性名中包含的数字(如"item1"), * 并尝试将这些属性值替换为对应类型数据中的实际对象引用。 * * @param {Object} data - 包含嵌套引用的原始数据对象 * @returns {Object} 处理后的数据对象,其中引用已被解析为实际对象 */ function resolveDataReferences(data) { const keys = Object.keys(data); for (const key of keys) { const entities = data[key]; for (const entity of entities) { for (const attribute in entity) { if (entity.hasOwnProperty(attribute)) { // 修复:统一使用复数形式查找引用类型 let refType = attribute.replace(/\d/g, ''); // 尝试直接查找复数形式 if (!data[refType] && data[`${refType}s`]) { refType = `${refType}s`; } if (Array.isArray(entity[attribute])) { entity[attribute] = entity[attribute].map(item => data[refType]?.find(updateItem => updateItem.id === item.id) || item ); } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) { entity[attribute] = data[refType]?.find(updateItem => updateItem.id === entity[attribute].id) || entity[attribute]; } } } } } return data; } // 解析单个实体的数据引用 /** * 解析数据引用关系 * * 该函数用于处理实体对象与数据源之间的引用关系,自动匹配并更新实体中的引用字段。 * * @param {Object} entity - 需要处理的实体对象 * @param {Object} data - 包含引用数据的数据源对象 * @returns {Object} 处理后的实体对象 * * 功能说明: * 1. 遍历实体对象的每个属性 * 2. 如果属性值是数组,则尝试在数据源中查找匹配项更新数组元素 * 3. 如果属性值是对象,则尝试在数据源中查找匹配项更新该对象 * 4. 自动处理单复数形式的数据源键名 */ function resolveDataReference(entity, data) { for (const attribute in entity) { if (entity.hasOwnProperty(attribute)) { // 修复:统一使用复数形式查找引用类型 let refType = attribute.replace(/\d/g, ''); // 尝试直接查找复数形式 if (!data[refType] && data[`${refType}s`]) { refType = `${refType}s`; } if (Array.isArray(entity[attribute])) { entity[attribute] = entity[attribute].map(item => data[refType]?.find(updateItem => updateItem.id === item.id) || item ); } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) { entity[attribute] = data[refType]?.find(updateItem => updateItem.id === entity[attribute].id) || entity[attribute]; } } } return entity; } /** * 微信小程序数据管理器类 * 负责与后端API通信并管理数据,适配微信小程序环境 */ /** * 小程序数据管理类 * * 该类负责管理小程序的所有数据,包括网络数据和本地数据,并提供数据同步、CRUD操作等功能。 * * @property {Object} networkData - 从网络获取的数据 * @property {Object} localData - 本地存储的数据 * @property {Object} data - 合并后的数据(本地数据优先) * @property {boolean} isSyncing - 是否正在同步数据 * @property {Date} lastSync - 最后同步时间 * @property {Object} callbacks - 回调函数集合 * @property {Promise} syncQueue - 同步队列 * @property {Object} entiyeText - 实体重复提示文本 * @property {number} syncInterval - 自动同步间隔(毫秒) * @property {string} storageKey - 本地存储键名 * * @param {string} baseUrl - API基础URL * * 功能包括: * - 数据自动同步 * - 本地数据持久化 * - 数据合并策略 * - 实体CRUD操作 * - 重复检查 * - 回调机制 */ /** * 懒加载处理器 * 负责管理实体类之间的关联依赖,实现按需加载 */ class LazyLoader { constructor(dataManager) { this.dataManager = dataManager; this.cache = new Map(); } /** * 创建实体代理 * @param {Object} entity 实体对象 * @param {string} entityType 实体类型 * @returns {Proxy} 返回代理后的实体 */ createProxy(entity, entityType) { const handler = { get: (target, prop) => { // 如果是普通属性,直接返回 if (typeof target[prop] !== 'object' || target[prop] === null) { return target[prop]; } // 如果是关联对象,懒加载 if (prop.endsWith('_ref')) { const refType = prop.replace('_ref', ''); return this.loadReference(target[prop], refType); } // 如果是关联数组,懒加载 if (prop.endsWith('_refs')) { const refType = prop.replace('_refs', ''); return this.loadReferences(target[prop], refType); } return target[prop]; } }; return new Proxy(entity, handler); } /** * 加载单个关联引用 */ async loadReference(ref, refType) { if (!ref || !ref.id) return null; const cacheKey = `${refType}_${ref.id}`; if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey); } const entities = this.dataManager.data[`${refType}s`] || []; let entity = entities.find(e => e.id === ref.id); if (!entity) { await this.dataManager.syncData(); entity = (this.dataManager.data[`${refType}s`] || []) .find(e => e.id === ref.id); } if (entity) { const proxy = this.createProxy(entity, refType); this.cache.set(cacheKey, proxy); return proxy; } return null; } /** * 加载多个关联引用 */ async loadReferences(refs, refType) { if (!Array.isArray(refs)) return []; return Promise.all( refs.map(ref => this.loadReference(ref, refType)) ); } } class MiniProgramDataManager { // 修复:合并重复的构造函数 constructor(baseUrl) { this.baseUrl = baseUrl; this.debug = true; // 调试模式开关 this.requestCount = 0; // 请求计数器 // 数据结构定义 this._rawData = { bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], dingdan_bancais: [], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [], _lastModified: null, _lastSync: null }; // 初始化网络状态 this.networkAvailable = false; this.checkNetwork().then(type => { this.networkAvailable = type !== 'none'; }); this.lazyLoader = new LazyLoader(this); // 仅从本地存储加载数据,不自动同步 this.loadDataFromStorage(); this.isSyncing = false; this.lastSync = null; this.callbacks = { all: [], bancais: [], dingdan: [], mupi: [], chanpin: [], kucun: [], chanpin_zujian: [], dingdan_bancai: [], zujian: [], caizhi: [], dingdan_chanpin: [], user: [], jinhuo: [] }; this.syncQueue = Promise.resolve(); this.entiyeText = { bancai: '板材已存在', dingdan: '订单已存在', mupi: '木皮已存在', chanpin: '产品已存在', kucun: '已有库存记录', chanpin_zujian: '产品已有该组件', dingdan_bancai: '', zujian: '组件已定义过了', caizhi: '材质已定义过了', dingdan_chanpin: '订单下已有该产品', user: '' }; this.syncInterval = 5 * 60 * 1000; // 5分钟 this.storageKey = 'miniProgramData'; // 本地存储的键名 } get data() { // 创建数据代理 const handler = { get: (target, prop) => { // 处理特殊属性 if (prop.startsWith('_')) { return target[prop]; } // 处理数组类型的实体集合 if (Array.isArray(target[prop])) { return target[prop].map(item => this.lazyLoader.createProxy(item, prop.replace(/s$/, '')) ); } // 默认返回原始值 return target[prop]; }, // 保持其他操作不变 set: (target, prop, value) => { target[prop] = value; return true; } }; return new Proxy(this._rawData, handler); } // 添加显式初始化方法 async initialize() { // 启动自动同步 this.startAutoSync(); // 执行首次数据同步 await this.syncData(); } /** * 启动自动同步定时器 * 每隔syncInterval毫秒检查并执行数据同步 * 如果已有同步任务进行中则跳过 */ startAutoSync() { if (this.autoSyncTimer) clearInterval(this.autoSyncTimer); this.autoSyncTimer = setInterval(() => { if (!this.isSyncing) this.syncData(); }, this.syncInterval); } /** * 停止自动同步 */ stopAutoSync() { clearInterval(this.autoSyncTimer); } /** * 检查网络状态 */ checkNetwork() { return new Promise((resolve) => { wx.getNetworkType({ success: (res) => { resolve(res.networkType); }, fail: () => { resolve('unknown'); } }); }); } /** * 获取所有数据(全量或增量) * @async * @param {string} [since] - 增量获取的时间戳,不传则全量获取 * @returns {Promise<boolean>} 是否获取成功 * @description * - 根据since参数决定全量或增量获取数据 * - 增量获取时会合并新数据到现有数据 * - 全量获取会直接替换现有数据 * - 成功后会更新同步时间并保存到本地存储 * - 失败时会触发错误回调,若无历史数据则抛出错误 */ async fetchAll(since) { try { console.log(since ? `增量获取数据(自${since})...` : '全量获取数据...'); const params = since ? { since } : {}; const result = await this.request('/app/all', 'GET', params); const resolvedData =result ; // 更新networkData Object.keys(this._rawData).forEach(key => { if (key.startsWith('_')) return; if (resolvedData[key]) { if (since) { // 增量更新: 合并新数据到现有数据 resolvedData[key].forEach(newItem => { const index = this._rawData[key].findIndex(item => item.id === newItem.id); if (index >= 0) { this._rawData[key][index] = newItem; } else { this._rawData[key].push(newItem); } }); } else { // 全量更新: 直接替换 this._rawData[key] = resolvedData[key]; } } }); // 更新同步时间 this.lastSync = new Date(); this._rawData._lastSync = this.lastSync.toISOString(); // 保存到本地存储 this.saveDataToStorage(); this.triggerCallbacks('refresh', 'all', this.data); return true; } catch (error) { console.error('Fetch error:', error); this.triggerCallbacks('fetch_error', 'all', { error }); // 失败时尝试使用本地数据 if (!this.lastSync) { throw new Error('初始化数据获取失败'); } return false; } } /** * 微信小程序API请求封装 */ request(url, method = 'GET', data = null, retryCount = 3) { return new Promise((resolve, reject) => { const makeRequest = (attempt) => { const fullUrl = `${this.baseUrl}${url}`; if (this.debug) { console.log(`[请求] ${method} ${fullUrl}`, { attempt, data, timestamp: new Date().toISOString() }); } wx.request({ url: fullUrl, method, data, header: { 'Content-Type': 'application/json' }, success: (res) => { if (this.debug) { console.log(`[响应] ${fullUrl}`, { status: res.statusCode, data: res.data, headers: res.header }); } // 修复:更灵活的响应格式处理 if (!res.data) { const err = new Error('空响应数据'); if (attempt < retryCount) { this.retryRequest(makeRequest, attempt, retryCount, err); } else { reject(err); } return; } // 修复:支持多种成功状态码和响应格式 const isSuccess = res.statusCode >= 200 && res.statusCode < 300; const hasData = res.data && (res.data.data !== undefined || typeof res.data === 'object'); if (isSuccess && hasData) { resolve(res.data.data || res.data); } else { const errMsg = res.data.message || res.data.text || 'API错误'; const err = new Error(errMsg); if (attempt < retryCount) { this.retryRequest(makeRequest, attempt, retryCount, err); } else { reject(err); } } }, fail: (err) => { if (this.debug) { console.error(`[失败] ${fullUrl}`, err); } const error = new Error(`网络请求失败: ${err.errMsg || '未知错误'}`); if (attempt < retryCount) { this.retryRequest(makeRequest, attempt, retryCount, error); } else { reject(error); } } }); }; makeRequest(1); }); } retryRequest(makeRequest, attempt, retryCount, error) { const delay = 1000 * attempt; console.warn(`请求失败 (${attempt}/${retryCount}), ${delay}ms后重试:`, error.message); setTimeout(() => makeRequest(attempt + 1), delay); } /** * 注册回调函数 */ registerCallback(entity, callback) { if (!this.callbacks[entity]) { this.callbacks[entity] = []; } this.callbacks[entity].push(callback); } /** * 注销回调函数 */ unregisterCallback(entity, callback) { if (!this.callbacks[entity]) return; const index = this.callbacks[entity].indexOf(callback); if (index !== -1) { this.callbacks[entity].splice(index, 1); } } /** * 触发回调函数 */ triggerCallbacks(operation, entity, data) { this.callbacks.all.forEach(cb => cb(operation, entity, data)); if (this.callbacks[entity]) { this.callbacks[entity].forEach(cb => cb(operation, data)); } } /** * 检查重复实体 */ checkDuplicate(entity, data) { // 修复:确保引用已解析 const resolvedData = resolveDataReference(data, this.data); switch (entity) { case 'bancai': return this.data.bancais.some(b => b.houdu === resolvedData.houdu && b.caizhi?.id === resolvedData.caizhi?.id && b.mupi1?.id === resolvedData.mupi1?.id && b.mupi2?.id === resolvedData.mupi2?.id ); case 'caizhi': return this.data.caizhis.some(c => c.name === resolvedData.name); case 'mupi': return this.data.mupis.some(m => m.name === resolvedData.name && m.you === resolvedData.you); case 'chanpin': return this.data.chanpins.some(c => c.bianhao === resolvedData.bianhao); case 'zujian': return this.data.zujians.some(z => z.name === resolvedData.name); case 'dingdan': return this.data.dingdans.some(d => d.number === resolvedData.number); case 'chanpin_zujian': return this.data.chanpin_zujians.some(cz => cz.chanpin?.id === resolvedData.chanpin?.id && cz.zujian?.id === resolvedData.zujian?.id ); case 'dingdan_chanpin': return this.data.dingdan_chanpins.some(dc => dc.dingdan?.id === resolvedData.dingdan?.id && dc.chanpin?.id === resolvedData.chanpin?.id ); case 'dingdan_bancai': return this.data.dingdan_bancais.some(db => db.dingdan?.id === resolvedData.dingdan?.id && db.chanpin?.id === resolvedData.chanpin?.id && db.zujian?.id === resolvedData.zujian?.id && db.bancai?.id === resolvedData.bancai?.id ); case 'user': return this.data.users.some(u => u.name === resolvedData.name); default: return false; } } /** * CRUD操作通用方法 */ async crudOperation(operation, entity, data) { try { // 使用微信请求API替代fetch const result = await this.request(`/app/${operation}/${entity}`, 'POST', data); this.updateLocalData(operation, entity, result || data); this.triggerCallbacks(operation, entity, result || data); return result; } catch (error) { console.error('CRUD error:', error); this.triggerCallbacks(`${operation}_error`, entity, { data, error: error.message }); throw error; } } /** * 更新本地数据 */ /** * 更新本地数据 * @param {string} operation - 操作类型: 'add' | 'update' | 'delete' * @param {string} entity - 实体名称 * @param {Object} newData - 新数据对象(包含id字段) * @description 根据操作类型对本地数据进行增删改操作 */ updateLocalData(operation, entity, newData) { const key = `${entity}s`; const entities = this._rawData[key]; // 确保新数据的引用已解析 const resolvedData = resolveDataReference(newData, this._rawData); switch (operation) { case 'add': entities.push(resolvedData); break; case 'update': const index = entities.findIndex(item => item.id === resolvedData.id); if (index !== -1) { // 修复:使用对象展开操作符确保属性完整覆盖 entities[index] = { ...entities[index], ...resolvedData }; } else { entities.push(resolvedData); } break; case 'delete': const deleteIndex = entities.findIndex(item => item.id === resolvedData.id); if (deleteIndex !== -1) { entities.splice(deleteIndex, 1); } break; } // 更新最后修改时间 this._rawData._lastModified = new Date().toISOString(); // 保存修改后的数据到本地存储 this.saveDataToStorage(); } /** * 同步数据 */ /** * 同步数据方法 * 该方法用于异步获取所有数据,并处理同步过程中的并发请求 * 如果同步正在进行中,会将请求标记为待处理(pendingSync) * 同步完成后会自动处理待处理的请求 * @async * @throws {Error} 当获取数据失败时会抛出错误并记录日志 */ async syncData() { if (this.isSyncing) { this.pendingSync = true; return; } this.isSyncing = true; try { // 1. 先加载本地数据 this.loadDataFromStorage(); // 2. 获取最后同步时间,用于增量更新 const since = this._rawData._lastSync || null; // 3. 获取增量数据 await this.fetchAll(since); // 4. 保存更新后的数据到本地存储 this.saveDataToStorage(); // 5. 触发数据更新回调 this.triggerCallbacks('refresh', 'all', this.data); } catch (error) { console.error('Sync failed:', error); this.triggerCallbacks('sync_error', 'all', { error }); // 失败时尝试使用本地数据 if (!this._rawData._lastSync) { throw new Error('初始化数据同步失败'); } } finally { this.isSyncing = false; if (this.pendingSync) { this.pendingSync = false; this.syncData(); } } } /** * 从本地存储加载数据 * 使用微信小程序的同步存储API获取之前保存的数据 */ loadDataFromStorage() { try { const storedData = wx.getStorageSync(this.storageKey); if (storedData) { // 修复:加载到_rawData而非data代理对象 this._rawData = storedData; } } catch (error) { console.error('加载本地存储数据失败:', error); // 提供默认空数据 this._rawData = { bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], dingdan_bancais: [], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [], _lastModified: null, _lastSync: null }; } } /** * 保存数据到本地存储 * 使用微信小程序的同步存储API持久化当前数据 */ saveDataToStorage() { try { // 修复:保存_rawData而非localData wx.setStorageSync(this.storageKey, this._rawData); } catch (error) { console.error('保存数据到本地存储失败:', error); // 提示用户或执行降级策略 wx.showToast({ title: '数据保存失败,请稍后重试', icon: 'none' }); } } /** * 添加实体 */ /** * 添加实体数据 * @async * @param {string} entity - 实体类型 * @param {Object} data - 要添加的实体数据 * @returns {Promise} 返回CRUD操作结果 * @throws {Error} 如果数据已存在则抛出错误 */ async addEntity(entity, data) { if (this.checkDuplicate(entity, data)) { const errorMsg = `${this.entiyeText[entity]}`; this.triggerCallbacks('duplicate_error', entity, { data, error: errorMsg }); throw new Error(errorMsg); } return this.crudOperation('add', entity, data); } /** * 更新实体 */ async updateEntity(entity, data) { return this.crudOperation('update', entity, data); } /** * 删除实体 */ async deleteEntity(entity, id) { return this.crudOperation('delete', entity, { id }); } getBancaisForZujian(zujianId) { const dingdan_bancais = this.data.dingdan_bancais.filter(db => db.zujian?.id == zujianId); return dingdan_bancais.map(db => db.bancai).filter(Boolean); } /** * 获取板材的库存信息 */ getKucunForBancai(bancaiId) { return this.data.kucuns.find(k => k.bancai?.id == bancaiId); } } // 导出模块 module.exports = MiniProgramDataManager;
最新发布
07-15
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值