Redux源码解析系列 (二)-- 牛鼻的createStore,【面试必备】

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

let isSubscribed = true

ensureCanMutateNextListeners()

// 直接将监听的函数放进nextListeners里

nextListeners.push(listener)

return function unsubscribe() {

// 如果已经移除了就直接返回

if (!isSubscribed) {

return

}

isSubscribed = false

ensureCanMutateNextListeners()

// 没有移除的话,先找到位置,通过splice移除

const index = nextListeners.indexOf(listener)

nextListeners.splice(index, 1)

}

}

在使用的时候就可以:

const unsubscribe = store.subscribe(() =>

console.log(store.getState())

)

unsubscribe()

dispatch

dispatch


dispatch 作为一个重点函数~ 其实它的作用就是触发状态的改变。

参数:action(object),它是一个描述发生了什么的对象,其中type是必须的属性。

返回:这个传入的object

function dispatch(action) {

if (!isPlainObject(action)) {

throw new Error(

'Actions must be plain objects. ’ +

‘Use custom middleware for async actions.’

)

}

//

if (typeof action.type === ‘undefined’) {

throw new Error(

'Actions may not have an undefined “type” property. ’ +

‘Have you misspelled a constant?’

)

}

// 防止多次dispatch请求同时改状态,一定是前面的dispatch结束之后,才dispatch下一个

if (isDispatching) {

throw new Error(‘Reducers may not dispatch actions.’)

}

try {

isDispatching = true

currentState = currentReducer(currentState, action)

} finally {

isDispatching = false

}

// 在dispatch的时候,又将nextListeners 赋值回currentListeners,

const listeners = currentListeners = nextListeners

for (let i = 0; i < listeners.length; i++) {

const listener = listeners[i]

listener()

}

return action

}

在上面一系列完成之后,需要初始化appState的状态。当INIT action被dispatched 的时候,每个reducer都会return回它的初始状态

dispatch({ type: ActionTypes.INIT })

自问自答环节

======

为什么createStore中既存在currentListeners也存在nextListeners?


在上面的源码中,createStore函数为了保存store的订阅者,不仅保存了当前的订阅者currentListeners而且也保存了nextListeners。createStore中有一个内部函数ensureCanMutateNextListeners:

function ensureCanMutateNextListeners() {

if (nextListeners === currentListeners) {

nextListeners = currentListeners.slice()

}

}

这个函数实质的作用是确保可以改变nextListeners,如果nextListeners与currentListeners一致的话,将currentListeners做一个拷贝赋值给nextListeners,然后所有的操作都会集中在nextListeners,比如我们看订阅的函数subscribe:

function subscribe(listener) {

// …

let isSubscribed = true

ensureCanMutateNextListeners()

nextListeners.push(listener)

return function unsubscribe() {

// …

ensureCanMutateNextListeners()

const index = nextListeners.indexOf(listener)

nextListeners.splice(index, 1)

}

我们发现订阅和解除订阅都是在nextListeners做的操作,然后每次dispatch一个action都会做如下的操作:

function dispatch(action) {

try {

isDispatching = true

currentState = currentReducer(currentState, action)

} finally {

isDispatching = false

}

// 相当于currentListeners = nextListeners const listeners = currentListeners

const listeners = currentListeners = nextListeners

for (let i = 0; i < listeners.length; i++) {

const listener = listeners[i]

listener()

}

return action

}

我们发现在dispatch中做了const listeners = currentListeners = nextListeners,相当于更新了当前currentListenersnextListeners,然后通知订阅者,到这里我们不禁要问为什么要存在这个nextListeners?     其实代码中的注释也是做了相关的解释:

The subscriptions are snapshotted just before every dispatch() call.If you subscribe or unsubscribe while the listeners are being invoked, this will not have any effect on the dispatch() that is currently in progress.However, the next dispatch() call, whether nested or not, will use a more recent snapshot of the subscription list.

来让我这个六级没过的渣渣翻译一下: 订阅者(subscriptions)在每次dispatch()调用之前都是一份快照(snapshotted)。如果你在listener被调用期间,进行订阅或者退订,在本次的dispatch()过程中是不会生效的,然而在下一次的dispatch()调用中,无论dispatch是否是嵌套调用的,都将使用最近一次的快照订阅者列表。用图表示的效果如下:

我们从这个图中可以看见,如果不存在这个nextListeners这份快照的话,因为dispatch导致的store的改变,从而进一步通知订阅者,如果在通知订阅者的过程中发生了其他的订阅(subscribe)和退订(unsubscribe),那肯定会发生错误或者不确定性。例如:比如在通知订阅的过程中,如果发生了退订,那就既有可能成功退订(在通知之前就执行了nextListeners.splice(index, 1))或者没有成功退订(在已经通知了之后才执行了nextListeners.splice(index, 1)),这当然是不行的。因为nextListeners的存在所以通知订阅者的行为是明确的,订阅和退订是不会影响到本次订阅者通知的过程。

还是看不懂是什么意思?????一个简单粗俗的例子:

当在执行这段代码到第三个listener的时候:

for (let i = 0; i < listeners.length; i++) {

const listener = listeners[i]

listener()

}

你突然把第2个listener给splice了。这样的话此时上面的循环本来是执行完第三个要执行第四个了,但是由于数组中的第2个listener被splice掉了,所以数组后面的元素都要往前移动一个位置,这时数组的第四个listener就移动到原先第三个的位置了,数组的第五个listener就移动到原先第四个的位置了,因此循环本要执行第四个的,结果由于第四个往前移动了,实际执行的是原先的第五个,所以导致原先的第四个没有被执行。。

没错,上面讲的就是这样的!!!!哈哈哈明白了吧!!!!

但是这里又有一个问题了:

JavaScript不是单线程的吗?为啥在执行循环的时候,会执行unsubscribe()操作


百思不得其解的情况下,去Redux项目下开了一个issue,得到了维护者的回答:

得了,我们再来看看测试相关的代码吧。看完之后我了解到了。的确,因为JavaScript是单线程语言,不可能出现出现想上述所说的多线程场景,但是我忽略了一点,执行订阅者函数时,在这个回调函数中可以执行退订或者订阅事件。例如:

const store = createStore(reducers.todos)

const unsubscribe1 = store.subscribe(() => {

const unsubscribe2 = store.subscribe(()=>{})

})

这不就实现了在通知listener的过程中混入订阅subscribe与退订unsubscribe吗?

为什么Reducer中不能进行dispatch操作?


我们知道在reducer函数中是不能执行dispatch操作的。一方面,reducer作为计算下一次state的纯函数是不应该承担执行dispatch这样的操作。另一方面,即使你尝试着在reducer中执行dispatch,也并不会成功,并且会得到"Reducers may not dispatch actions."的提示。因为在dispatch函数就做了相关的限制:

function dispatch(action) {

if (isDispatching) {

throw new Error(‘Reducers may not dispatch actions.’)

}

try {

isDispatching = true

currentState = currentReducer(currentState, action)

} finally {

isDispatching = false

}

//…notice listener

}

在执行dispatch时就会将标志位isDispatching置为true。然后如果在currentReducer(currentState, action)执行的过程中由执行了dispatch,那么就会抛出错误(‘Reducers may not dispatch actions.’)。之所以做如此的限制,是因为在dispatch中会引起reducer的执行,如果此时reducer中又执行了dispatch,这样就落入了一个死循环,所以就要避免reducer中执行dispatch。

难道这样就够了吗?不,远远不够!

提前多熟悉阿里往年的面试题肯定是对面试有很大的帮助的,但是作为技术性职业,手里有实打实的技术才是你面对面试官最有用的利器,这是从内在散发出来的自信。

备战阿里时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是一些我学习期间觉得还是很不错的一些学习笔记

我为什么要写这篇文章呢,其实我觉得学习是不能停下脚步的,在网络上和大家一起分享,一起讨论,不单单可以遇到更多一样的人,还可以扩大自己的眼界,学习到更多的技术,我还会在csdn、博客、掘金等网站上分享技术,这也是一种学习的方法。

今天就分享到这里了,谢谢大家的关注,以后会分享更多的干货给大家!

阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服

阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服

image.png

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

己的眼界,学习到更多的技术,我还会在csdn、博客、掘金等网站上分享技术,这也是一种学习的方法。

今天就分享到这里了,谢谢大家的关注,以后会分享更多的干货给大家!

[外链图片转存中…(img-kiDD54co-1713646354211)]

[外链图片转存中…(img-Sl32rNbV-1713646354212)]

[外链图片转存中…(img-OlEtxhPy-1713646354212)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-Q3jq4z0z-1713646354213)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值