同步、异步
分析
调试demo
class App extends Component {
constructor(props) {
super(props);
this.state = {
number: 1
}
}
handelBtn = () => {
// 异步写法
this.setState({
number: this.state.number + 1
}, () => {
console.log(this.state.number, 'callback1');
})
console.log(this.state.number, 'log1');
this.setState({
number: this.state.number + 1
}, () => {
console.log(this.state.number, 'callback2');
})
console.log(this.state.number, 'log2');
this.setState({
number: this.state.number + 1
}, () => {
console.log(this.state.number, 'callback3');
})
console.log(this.state.number, 'log3');
// 同步写法
setTimeout(() => {
this.setState({
number: this.state.number + 1
}, () => {
console.log(this.state.number, 'callback1');
})
console.log(this.state.number, 'log1');
debugger
this.setState({
number: this.state.number + 1
}, () => {
console.log(this.state.number, 'callback2');
})
console.log(this.state.number, 'log2');
this.setState({
number: this.state.number + 1
}, () => {
console.log(this.state.number, 'callback3');
})
console.log(this.state.number, 'log3');
}, 0);
}
render () {
return (
<div>
<button onClick={() => this.handelBtn()} >+1</button>
<div>{this.state.number}</div>
</div>
)
}
}
输出
1 'log1'
1 'log2'
1 'log3'
2 'callback1'
2 'callback2'
2 'callback3'
流程图
关键阶段分析
1、点击事件会分发进入到批量处理函数
export function batchedUpdates<A, R>(fn: A => R, a: A): R {
const prevExecutionContext = executionContext;
// 将批量处理标记记录在executionContext中
executionContext |= BatchedContext;
try {
// 执行onclick
return fn(a);
} finally {
executionContext = prevExecutionContext;
// If there were legacy sync updates, flush them at the end of the outer
// most batchedUpdates-like method.
if (
executionContext === NoContext &&
// Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
!(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)
) {
resetRenderTimer();
// onClick全部处理完后执行
flushSyncCallbacksOnlyInLegacyMode();
}
}
}
从dipathcEvent到onClilck堆栈
2、执行onClick中的setState
const classComponentUpdater = {
isMounted,
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
// 生成此次setState的updape
const update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
// 将这次update放入filber中的更新列表中
enqueueUpdate(fiber, update, lane);
// 对filber进行调度
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
if (enableSchedulingProfiler) {
markStateUpdateScheduled(fiber, lane);
}
},
};
\
3、scheduleUpdateOnFiber,
- setState的执行告一段落,将这次更新挂在filber的更新队列中
- 根据executionContext是否打上批量跟新标签
- 执行onClick中的console.log函数,然后依次重复执行完三次,输出三次打印(异步)
- 当Click全部执行完后,回到batchedUpdates中的finally阶段(异步)
- 执行flushSyncCallbacksOnlyInLegacyMode(同步)
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
): FiberRoot | null {
//...
// 由于在batchedUpdates中executionContext加上了批量处理标签为1,而NoContext等于0,无法进入判断函数。
// 同步的情况下未经过batchedUpdates,可直接进入判断执行flushSyncCallbacksOnlyInLegacyMode
if (
lane === SyncLane &&
executionContext === NoContext &&
(fiber.mode & ConcurrentMode) === NoMode &&
// Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
!(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)
) {
resetRenderTimer();
// 同步执行setState
flushSyncCallbacksOnlyInLegacyMode();
}
return root;
}
4、 flushSyncCallbacksOnlyInLegacyMode到processUpdateQueue
- (异步) 存储的三个setState对应的update,最后将运行effect链,执行三个setState的回调函数
- (同步) 此时update链中只有当前的setState更新,后面的异步更新一样,执行完update链后再执行effect链
执行完后再执行console.log操作,再继续执行setState,所以每个setState是一个完整更新回合,没有存储在update链中异步同一执行。
\
export function processUpdateQueue<State>(
workInProgress: Fiber,
props: any,
instance: any,
renderLanes: Lanes,
): void {
const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
let firstBaseUpdate = queue.firstBaseUpdate;
let lastBaseUpdate = queue.lastBaseUpdate;
let pendingQueue = queue.shared.pending;
if (pendingQueue !== null) {
queue.shared.pending = null;
const lastPendingUpdate = pendingQueue;
const firstPendingUpdate = lastPendingUpdate.next;
lastPendingUpdate.next = null;
// Append pending updates to base queue
if (lastBaseUpdate === null) {
firstBaseUpdate = firstPendingUpdate;
} else {
lastBaseUpdate.next = firstPendingUpdate;
}
lastBaseUpdate = lastPendingUpdate;
if (firstBaseUpdate !== null) {
// Iterate through the list of updates to compute the result.
let newState = queue.baseState;
// TODO: Don't need to accumulate this. Instead, we can remove renderLanes
// from the original lanes.
let newLanes = NoLanes;
let newBaseState = null;
let newFirstBaseUpdate = null;
let newLastBaseUpdate = null;
let update = firstBaseUpdate;
do {
const updateLane = update.lane;
const updateEventTime = update.eventTime;
// 将setState的回调函数放入effects链中
const callback = update.callback;
if (
callback !== null &&
// If the update was already committed, we should not queue its
// callback again.
update.lane !== NoLane
) {
workInProgress.flags |= Callback;
const effects = queue.effects;
if (effects === null) {
queue.effects = [update];
} else {
effects.push(update);
}
}
}
// 对下一个update进行处理
update = update.next;
if (update === null) {
}
} while (true);
if (newLastBaseUpdate === null) {
newBaseState = newState;
}
}
总结
- 异步和同步主要区别是在看flushSyncCallbacksOnlyInLegacyMode函数在那调用
- 异步:由于executionContext在打上了批量更新标签,所以会等到onClick内函数全部执行完后在finally执行flushSyncCallbacksOnlyInLegacyMode函数,此时的三次setState会记录在updateQueue中,最后异步依次执行。
- 同步,由于setState触发的上下文是在setTimeout中,没有打上了任何标签,在scheduleUpdateOnFiber中进入判断执行flushSyncCallbacksOnlyInLegacyMode函数,此时立即执行updateQueue(只当当前一个updata),整个update运行完后在到console,log函数。
数据合并
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
render() {
return (
<div>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={e => this.increment()}>+1</button>
</div>
)
}
increment() {
// 1.setState本身被合并,最终运算还是1
this.setState({
counter: this.state.counter + 1
});
this.setState({
counter: this.state.counter + 1
});
this.setState({
counter: this.state.counter + 1
});
// 2.setState合并时进行累加,最终运算3
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
}
}
分析
1、setState
- 此时的partialState是{ count : this.state.counter + 1 }的结果,{ count : 2 }。
- 在enqueueSetState中将partialState挂在在update.payload中。
- 根据批量处理方式(异步),三次运行setState时,this.state并没变化,所以三个update.payload都是{ count : 2 }。
Component.prototype.setState = function (partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
const classComponentUpdater = {
isMounted,
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const eventTime = requestEventTime();
const lane = requestUpdateLane(fiber);
const update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.callback = callback;
}
enqueueUpdate(fiber, update, lane);
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
},
};
2、ReactUpdateQueue
- 通过do while循环将updateQueue队列依次执行,并每次使用newStata更新state
- 在UpdateState过程中分setState传入是对象还是函数
- 函数:会将更新前的preState传入函数中,partialState等于函数返回值。
- 对象:直接将update.payload赋值给partialState,由1中可知三次的update.payload都是{ count : 2 }。
export function processUpdateQueue<State>(
workInProgress: Fiber,
props: any,
instance: any,
renderExpirationTime: ExpirationTime,
): void {
// ...
if (baseQueue !== null) {
let first = baseQueue.next;
let newState = queue.baseState;
if (first !== null) {
let update = first;
do {
//...
newState = getStateFromUpdate(
workInProgress,
queue,
update,
newState,
props,
instance,
);
//...
update = update.next;
if (update === null || update === first) {
pendingQueue = queue.shared.pending;
if (pendingQueue === null) {
break;
} else {
update = baseQueue.next = pendingQueue.next;
pendingQueue.next = first;
queue.baseQueue = baseQueue = pendingQueue;
queue.shared.pending = null;
}
}
} while (true);
}
//...
}
function getStateFromUpdate<State>(
workInProgress: Fiber,
queue: UpdateQueue<State>,
update: Update<State>,
prevState: State,
nextProps: any,
instance: any,
): any {
console.log('123');
switch (update.tag) {
// Intentional fallthrough
case UpdateState: {
const payload = update.payload;
let partialState;
if (typeof payload === 'function') {
// 情况二,使用方法调用更新state
// 第一轮:prevState:0,partialState:1
// 第二轮:prevState:1,partialState:2
// 第三轮:prevState:2,partialState:3
partialState = payload.call(instance, prevState, nextProps);
if (__DEV__) {
} else {
// Partial state object
partialState = payload;
}
if (partialState === null || partialState === undefined) {
// Null and undefined are treated as no-ops.
return prevState;
}
// 情况一:使用对象形式更行
// 由于partialState传入的是this.state+1,而this.state在循环队列中并没有更行
// 第一轮 Object.assign({}, prevState:0, partialState:1);
// 第二轮 Object.assign({}, prevState:1, partialState:1);
// 第三轮 Object.assign({}, prevState:1, partialState:1);
// 最终还是返回1
return Object.assign({}, prevState, partialState);
}
case ForceUpdate: {
hasForceUpdate = true;
return prevState;
}
}
return prevState;
}