有些需求中,不能无限的等待 Promise,需要设置一个超时时间,但是原生函数又没有超时设置,于是手动实现一个 PromiseWithTimeout
市面上 react 的状态管理工具非常多,例如:redux、mobx、recoil,让接触 react 的同学目不暇接。
在看这些状态管理工具的文档时,并不能有效理解这些状态管理工具的差异,更不太清楚什么时候应该用什么状态管理工具,于是出现了这篇教程。
这篇教程对比了市面上主流的一些 react 状态管理工具,通过 在一个业务场景下,用不同 react 的状态管理工具来实现,用这种方式来对比不同工具写法上的差异,帮助读者更深入理解 react 状态管理工具。
react 挑选状态管理时有哪些指标? 细粒度 render 派生状态(联级派生) 异步(联级异步) 在非 react 上下文也可以订阅和更新 状态管理的范围 疑问 后端有没有状态管理工具? 首先有状态的地方就有状态管理,后端当然也不例外。
前端常常聊的状态管理只是一个狭义上的概念,指的是**一种实现跨组件通信和数据共享的方式。**它的特殊之处在于状态变更后自动更新用到该状态的相应组件。最好可以做到细粒度的更新,即只更新真正需要更新的 component.
vue 和 angular 为什么没有像 react 这样纷杂的这么多种状态管理工具? vue 和 angular 的数据都是响应式的,当数据变更时,什么时候刷新视图,刷新的粒度怎么样,开发者无需关心,这一切交给框架本身去做就好了。
所以 vue 基本用 vuex 就够了(最近又出了个 Pinia);angular 的数据管理交给 service 也就够了。
而 react 不一样,react 认为 UI 本身只是一个函数(UI = fn(state)),状态怎么变更,什么时候变更,react 把这些权利完全交给了开发者。这也就给各种状态管理发挥和想象的空间。造成 react 社区状态管理工具百家争鸣的现象。
react 状态管理工具的门派? 没有状态管理工具:直接用 props 或者 context 单项数据流:redux、zustand 双向绑定:mobx、valtio 状态原子化:jotai、recoil 有限状态机:xstate 其中 单项数据流、双向绑定、状态原子化 本质上都是发布订阅模式。
有些需求中,有取消 promise 的需求, 但是原生函数又没有取消的方法,于是手动实现一个 getPromiseWithAbort
感觉其内部用了 `ErrorBoundary`, 或者 `try-catch`。当数据没有加载时,throw 一个状态为 `pending` 的 `promise`, Suspense 内部可能会记住这个 `promise`, 当 `promise` 的状态改为 `fulfilled` 时,Suspense 会触发更新一下自己的子组件,这样就能 render 正确的数据。
阮一峰在 ES6 入门 中提到 ES6 模块与 CommonJS 模块有一些重大的差异:
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。 CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。 再细读上面阮老师提到的差异,会产生诸多疑问:
为什么 CommonJS 模块输出的是一个值的拷贝?其具体细节是什么样子的? 什么叫 运行时加载? 什么叫 编译时输出接口? 为什么 ES6 模块输出的是值的引用? 于是就有了这篇文章,力求把 ESM 模块 和 CommonJS 模块 讨论清楚。
CommonJS 产生的历史背景 CommonJS 由 Mozilla 工程师 Kevin Dangoor 于 2009 年 1 月创立,最初命名为 ServerJS。2009 年 8 月,该项目更名为 CommonJS。旨在解决 Javascript 中缺少模块化标准的问题。
Node.js 后来也采用了 CommonJS 的模块规范。
由于 CommonJS 并不是 ECMAScript 标准的一部分,所以 类似 module 和 require 并不是 JS 的关键字,仅仅是对象或者函数而已,意识到这一点很重要。
我们可以在打印 module、require 查看细节:
互斥锁和自旋锁 最底层的两种是 互斥锁 和 自旋锁,有很多高级的锁都是基于它们实现的,可以认为它们是各种锁的基石。
他们两的区别是:
互斥锁加锁失败后,内核会将该线程置为「休眠」状态,等到锁被释放后,内核会在合适的时机唤醒线程 自旋锁加锁失败后,线程会一直轮训内核,查看锁的状态;如果锁是空闲的,将锁设置为当前线程持有 拿点外卖打比方,互斥锁会打电话问店老板外卖有没有做好,如果外卖没好,互斥锁不会再打电话,而是会等待店老板的回话,老板说好了再去拿。而自旋锁如果接到外卖没好的回答,则会一直打电话询问老板饭有没有做好,一直等到老板回答做好为止。
乐观锁与悲观锁 前面提到的互斥锁、自旋锁,都是属于悲观锁。
悲观锁做事比较悲观,它认为多线程同时修改共享资源的概率比较高,于是很容易出现冲突,所以访问共享资源前,先要上锁。
那相反的,如果多线程同时修改共享资源的概率比较低,就可以采用乐观锁。
乐观锁做事比较乐观,它假定冲突的概率很低,它的工作方式是:先修改完共享资源,再验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作。
放弃后如何重试,这跟业务场景息息相关,虽然重试的成本很高,但是冲突的概率足够低的话,还是可以接受的。
可见,乐观锁的心态是,不管三七二十一,先改了资源再说。另外,你会发现乐观锁全程并没有加锁,所以它也叫无锁编程。
这里举一个场景例子:在线文档。
我们都知道在线文档可以同时多人编辑的,如果使用了悲观锁,那么只要有一个用户正在编辑文档,此时其他用户就无法打开相同的文档了,这用户体验当然不好了。
那实现多人同时编辑,实际上是用了乐观锁,它允许多个用户打开同一个文档进行编辑,编辑完提交之后才验证修改的内容是否有冲突。
还有我们常见的 SVN 和 Git 也是用了乐观锁的思想,先让用户编辑代码,然后提交的时候,通过版本号来判断是否产生了冲突,发生了冲突的地方,需要我们自己修改后,再重新提交。
乐观锁虽然去除了加锁解锁的操作,但是一旦发生冲突,重试的成本非常高,所以只有在冲突概率非常低,且加锁成本非常高的场景时,才考虑使用乐观锁。
简单总结就是:读的多,冲突几率小,乐观锁。写的多,冲突几率大,悲观锁。
参考链接 你说说互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景 乐观锁、悲观锁,这一篇就够了!