当前位置: 首页 > news >正文

uniapp 网络请求自动处理loading

文章目录

  • 背景
  • 整理思路
  • V1版本
  • V2版本
  • V3版本

背景

最近在写uniapp,发现执行网络请求的时候经常要处理Loading效果。

比如,在发送网络请求之前,触发Loadng;无论请求成功还是失败都要关闭Loading;请求失败的时候我们还要给用户一个友好的提示,比如“服务器打了个盹”。

每次都要手动处理,真的很麻烦;而且处理Loading的逻辑跟业务逻辑混在一起,也不便于维护。

因此,我打算把这个需求封装成要一个方法,就叫loadingRun

这个方法并不是一蹴而就的,经历了几个版本的迭代。我强烈建议你看完这篇文章,你一定能有所收获。

虽然是以uniapp代码演示的,但是思路是通用的。

整理思路

在封装之前,我们先看看封装之前代码长啥样:

uni.showLoading({mask: true})
uniCloud.database().collection('orders').doc(props.orderId).get({getOne: true}).then(res => {Object.assign(order, res.result.data)uni.hideLoading()
}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))
}).finally(_ => uni.hideLoading())

有些小伙伴可能不熟悉uniapp,所以我们解释一下部分代码:

  • uniCloud.database().collection('orders').doc(props.orderId).get({getOne: true})这是uniapp的uniCloud的操作,执行的是网络请求,返回的是一个promise。
  • uni.showLoading() uniapp 提供的显示Loading的api。
  • uni.hideLoading() uniapp 提供的关闭Loading的api。
  • uni.showToast() uniapp 提供的显示Toast提示api。

这些你都可以想象成自己熟悉的UI框架的操作。

我们需要把这个方法抽成一个通用的loadingRun方法,首先不管三七二十一,我们先把这个方法写出来:

function loadingRun() {uni.showLoading({mask: true})uniCloud.database().collection('orders').doc(props.orderId).get({getOne: true}).then(res => {Object.assign(order, res.result.data)uni.hideLoading()}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))}).finally(_ => uni.hideLoading())
}

然后我们就想,有哪些操作是可以提取出来,作为loadingRun的参数。
最容易想到的就是Object.assign(order, res.result.data)这行代码,这行代码是promise解决之后处理逻辑,我们可以把它提取成一个resolve参数:

function loadingRun(resolve) {uni.showLoading({mask: true})uniCloud.database().collection('orders').doc(props.orderId).get({getOne: true}).then(res => {resolve(res)uni.hideLoading()}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))}).finally(_ => uni.hideLoading())
}

还有什么可以提取的呢?uniCloud.database().collection('orders').doc(props.orderId).get({getOne: true})这个操作我们可以提取,因为的网络操作是不固定的。它是一个promise,我们先不管三七二十一,先用一promise来接收:

function loadingRun(promise, resolve) {uni.showLoading({mask: true})promise.then(res => {resolve(res)uni.hideLoading()}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))}).finally(_ => uni.hideLoading())
}

还能再抽吗?貌似toast提醒的文案可以抽出来,不过我们整站的提示都是统一的,所以就不抽了。

现在就可以了吗?显然不是,它还有问题,有大问题,我们接着聊。

V1版本

我们前面讲了封装loadingRun的思路,就是“抽”、“抽”、“抽”,先不管合理不合理,先抽再说。这样可以让我们最快的得到一个基本的骨架:

function loadingRun(promise, resolve) {uni.showLoading({mask: true})promise.then(res => {resolve(res)uni.hideLoading()}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))}).finally(_ => uni.hideLoading())
}

看着不错,实际有大问题,你知道是啥问题吗?

问题就是loadingRun直接接收了一个promise实例。

为啥直接接收就有问题呢?因为promise是创建时立即执行的,所以loadingRun执行的时候,promise可能已经执行完了,网络请求可能都返回了,此时在才显示Loading,黄瓜菜都凉了。

所以loadingRun不能从外部接收一个promise,我们需要在loadingRun内部创建这个promise。
我们可以让外部传入一个方法promiseGenerator,这个方法执行会产生一个promise:

function loadingRun(promiseGenerator, resolve) {uni.showLoading({mask: true})promiseGenerator().then(res => {resolve(res)uni.hideLoading()}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))}).finally(_ => uni.hideLoading())
}

至此我们获得了V1版本的loadingRun,我们前面代码可以用loadingRun来重构一下:

loadingRun(_ => uniCloud.database().collection('orders').doc(props.orderId).get({getOne: true}), res => Object.assign(order, res.result.data))

看起来没那么清晰对吧,我们再改进一下:

// 把获取订单的操作封装到一个方法中
function getOrderAsync(id) {return uniCloud.database().collection('orders').doc(id).get({getOne: true})
}
// 封装处理逻辑
function reactiveSetOrder(res) {Object.assign(order, res.result.data)
}

现在的loadingRun就清晰了:

loadingRun(getOrderAsync(props.id), reactiveSetOrder)

当然后面这个封装,不属于我门今天讨论的范畴。

V2版本

什么,还有V2版本,loadingRun还可以再优化吗?

还真是!现在的loadingRun接收一个promiseGeneratorpromiseGenerator产生一个promise实例。

如果我有一个很耗时的方法,但是它不返回promise,它也想用loadingRun怎么办呢?

现在V1版本还不够通用,我们需要让它变得更加通用。

我们可以这么处理,我们把promiseGenerator参数,换成更通用的func参数。如果它的结果是一个promise,那么就走原来的逻辑;否则,我们把func的执行结果用Promise.resolve封装成一个promise,走之前的逻辑。

这个叫参数归一化!

我们先定一个方法判断变量是不是一个promise:

function isPromiseLike(value) {return value != null && (typeof value === 'object' || typeof value === 'function')&& typeof value.then === 'function'
}

这里并不是直接用instanceof Promise判断,而是判断变量是不是满足Promise A+规范。

接下来我们改造loadingRun

function loadingRun(func, resolve) {uni.showLoading({mask: true})let ret = func()let promise = isPromiseLike(ret) ? ret : Promise.resolve(ret)promise.then(res => {resolve(res)uni.hideLoading()}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))}).finally(_ => uni.hideLoading())
}

现在我们可以用loadingRun处理耗时的非promise操作了,比如计算斐波那契数列:

loadingRun(_ => fibonacciRecursive(100000), val => console.log(`计算结果:${val}`))

V3版本

啊!还能再优化吗?

还真实。现在的loadingRun已经够用了,但是还有一个场景满足不了。

比如,当网络请求或者耗时操作报错了,我希望除了提示Toast,还要执行一个其他的操作,现在就没办法,因为promise实例没有返回出来。

因此改造也很简单,只需要把promise返回出来就行了。

function loadingRun(func, resolve) {uni.showLoading({ mask: true })let ret = func()let promise = isPromiseLike(ret) ? ret : Promise.resolve(ret)promise.then(res => {resolve(res)uni.hideLoading()}).catch(_ => {uni.hideLoading().then(_ => uni.showToast({icon: "fail",title: "服务器打了个盹"}))}).finally(_ => uni.hideLoading())return promise
}

然后我们就可以这样处理:

loadingRun(getOrderAsync(props.id), reactiveSetOrder).catch(_ => doSomething())

Game Over !!!

相关文章:

uniapp 网络请求自动处理loading

文章目录 背景整理思路V1版本V2版本V3版本 背景 最近在写uniapp,发现执行网络请求的时候经常要处理Loading效果。 比如,在发送网络请求之前,触发Loadng;无论请求成功还是失败都要关闭Loading;请求失败的时候我们还要…...

【Solidity】函数的使用

构造函数 构造函数仅在部署合约时调用一次,它的作用主要是初始化一些状态变量。 contract Demo {address public owner;uint public num;constructor(uint _num) {owner msg.sender;num _num;} }函数装饰器 函数装饰器可以在函数执行之前或之后插入代码逻辑&am…...

详解golang内存管理

介绍 要搞明白 Go 语言的内存管理,就必须先理解操作系统以及机器硬件是如何管理内存的。因为 Go 语言的内部机制是建立在这个基础之上的,它的设计,本质上就是尽可能的会发挥操作系统层面的优势,而避开导致低效情况。 操作系统内存管理 其实现在计算机内存管理的方式都是…...

C++ 线程 一些同步方式

C 线程一些同步方式 1.互斥锁(Mutex)2. 读写锁(Reader-Writer Lock)3. 信号量(Semaphore)4. 原子操作(Atomic)5. 屏障(Barrier)6. 条件变量(Condi…...

【开发语言】编译型语言和解释性语言有啥区别?

作为一名从业多年的程序员,对于编译型语言和解释型语言之间的区别有着深入的理解。这两种类型的编程语言在将源代码转换成可执行代码的过程中采用了不同的机制,这导致了它们在执行效率、跨平台性、安全性以及开发效率等方面存在一些差异。 编译型语言(Compiled Languages)…...

将A服务器上指定文件夹中的文件,批量同步到B服务器上

需求:最近有一个需求,需要定期将A服务器上的PDF文件,同步到B服务器上,于是便写个脚本记录一下! 下面是使用Python3脚本实现的方法 import os import paramikodef copy_pdf_files(source_ip, source_user, source_pas…...

2024.8.17

130124202408171002 DATE #:20240817 ITEM #:DOC WEEK #:SATURDAY DAIL #:捌月拾肆 TAGS < BGM "快哉风 -- 黄金玉米王" > < theme oi-language > < theme oi-graph theory > < [空] > < [空] >取次花丛懒回顾&#xff0c;半缘修道…...

十分钟搭建一个RTMP服务器

使用SRS搭建RTMP服务器 如果您需要搭建一个RTMP服务器&#xff0c;您可以使用SRS&#xff08;Simple-RTMP-Server&#xff09;来完成此任务。SRS是一个开源的RTMP服务器下面是一个简单的步骤指南&#xff1a; 获取srs srs官⽹&#xff1a;https://github.com/ossrs/srs 码云…...

Spring Boot解决循环注入问题

Spring Boot解决循环依赖注入问题 代码问题回显启动错误日志解决方案&#xff1a;使用事件驱动或通过 ApplicationContext 手动获取 Bean1. 事件驱动设计2. 使用 ApplicationContext 手动获取 Bean3. 拆分逻辑 总结 代码问题回显 现有代码1 在InterestService中依赖MemberInte…...

《数据挖掘》期末考核重点

1.数据预处理的目的与形式 数据预处理的目的是提供干净&#xff0c;简洁&#xff0c;准确的数据&#xff0c;以达到简化模型和提高算法泛化能力的目的&#xff0c;使挖掘过程更有效&#xff0c;更容易&#xff0c;提高挖掘效率和准确性。 2.数据预处理的形式 数据清理&#…...

Golang | Leetcode Golang题解之第334题递增的三元子序列

题目&#xff1a; 题解&#xff1a; func increasingTriplet(nums []int) bool {n : len(nums)if n < 3 {return false}first, second : nums[0], math.MaxInt32for i : 1; i < n; i {num : nums[i]if num > second {return true} else if num > first {second n…...

HarmonyOs编写一个案例实现一个照片选择(阶段进阶 四种需求 逐一完善)

需求1. .实现照片选择 并将选择好的照片展示出来 import { GoodItem } from ../06/modules;Entry Component struct PhotoPage {State message: string 实现一个相册;State List: GoodItem[] [{goods_name: dsfjlsjkfsf,goods_price: 100,goods_img: https://img1.baidu.com…...

洗衣机洗衣服一些知识

01智能:按衣物多少自动调节合适水位的标准洗涤程序 (需要30分钟时间) 02:大物:较大,较厚的衣服洗涤 03:轻柔:毛织品或内衣洗涤 04:快速:少量清污衣服洗涤 (13分钟) 05:浸泡:先浸泡一段时间再洗涤 06:单洗:只洗衣不脱水 07:单脱:只脱水不洗衣 08:洁桶:清洁洗衣桶 准备工作: (1)…...

探索文件系统:高效、可靠的文件管理与访问机制

文件系统的功能规划 内存就像是一个书包&#xff0c;容量有限&#xff0c;只能带着一部分东西。而图书馆则是一个专门存储和管理文件的地方&#xff0c;拥有更大的容量&#xff0c;并且可以永久保存文件。为了能够快速找到需要的文件&#xff0c;我们需要有一个书单来记录每本…...

启程与远征Ⅸ--优化生成式人工智能以满足业务需求的框架

生成类似人类的文本和语音曾经只存在于科幻小说中。但 GPT-3 和 PaLM 等大型语言模型 (LLM) 的快速发展让这一愿景更接近现实&#xff0c;解锁了从聊天机器人到内容创作等一系列有前景的商业应用。 然而&#xff0c;通用基础模型往往无法满足行业用例的需求。企业对其生成式 A…...

canal数据同步工具介绍与应用

canal服务 canal介绍canal版本与环境canal 服务集canal应用场景&#xff1a; canal常见问题xml配置问题连接认证问题jar版本问题连接问题 canal介绍 ‌1、Canal是‌阿里巴巴开源的‌MySQL增量数据订阅和消费工具&#xff0c;通过模拟MySQL的‌slave与‌master交互&#xff0c;捕…...

ubuntu18.04 设置静态地址

修改配置文件 sudo vim /etc/netplan/01-network-manager-all.yaml 代码如下&#xff1a; network: version: 2 renderer: NetworkManager ethernets: ens33: # 配置的网卡名称&#xff0c;可以使用ifconfig -a查看本机的网卡 dhcp4: no # 关闭动态IP设置 …...

jira敏捷开发管理工具视频教程Confluence工作流协同开发(2024)

正文&#xff1a; 随着Jira敏捷开发方法论的普及&#xff0c;Jira已经成为全球软件开发团队管理项目、任务和问题的首选工具。为了帮助团队更好地掌握Jira的核心功能&#xff0c;精心准备了一套全面开发技术及案例视频教程——《Jira敏捷开发管理工具视频教程Confluenc…...

【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法

文章目录 ServerSocket构造方法方法 Socket构造方法方法 回显服务器&#xff08;Echo Server&#xff09;1. 构造方法2. 建立连接processConnection 方法的创建1. 读取请求并解析2. 根据请求计算响应3. 把响应写回给客户端 3. 完整代码 客户端&#xff08;Echo Client&#xff…...

Python知识点:如何使用Boto3进行AWS服务管理

使用 boto3 来管理 AWS 服务是一个非常强大的方式&#xff0c;因为 boto3 是 AWS 提供的官方 Python SDK。下面是使用 boto3 管理 AWS 服务的基本步骤&#xff0c;包括设置、操作和常见的 AWS 服务示例。 1. 安装 boto3 首先&#xff0c;确保你已经安装了 boto3。可以使用 pi…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...