Skip to content

Commit

Permalink
chore: bump 3.5.15
Browse files Browse the repository at this point in the history
  • Loading branch information
fantasticsoul committed Dec 17, 2023
1 parent a10a743 commit a99d0c5
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 120 deletions.
2 changes: 1 addition & 1 deletion packages/helux-core/src/consts/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { VER as limuVer } from 'limu';

export const VER = '3.5.14';
export const VER = '3.5.15';

export const LIMU_VER = limuVer;

Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/src/factory/creator/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function currentDraftRoot() {
export const TRIGGERED_WATCH = {
current: () => CURRENT_TRIGGERED_WATCH,
set: (val: any) => (CURRENT_TRIGGERED_WATCH = val),
del: () => CURRENT_TRIGGERED_WATCH = '',
del: () => (CURRENT_TRIGGERED_WATCH = ''),
};

export const DEPS_CB = {
Expand Down
10 changes: 6 additions & 4 deletions packages/helux-core/src/factory/creator/deadCycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function alertDepKeyDeadCycleErr(internal: TInternal, dcErrorInfo: { err:
* 2 matate(draft=>draft+=1)
* 等场景的死循环
*/
export function probeDepKeyDeadCycle(internal: TInternal, fnCtx: IFnCtx, changedDepKeys: string[]): { err: Error | null; tipFn: Fn } {
export function probeDepKeyDeadCycle(internal: TInternal, fnCtx: IFnCtx, changedDepKeys: string[]): boolean {
const { depKeys, subFnInfo } = fnCtx;
let shortArr = fnCtx.depKeys;
let longArr = changedDepKeys;
Expand All @@ -106,14 +106,16 @@ export function probeDepKeyDeadCycle(internal: TInternal, fnCtx: IFnCtx, changed
longArr = depKeys;
}

let dcErrorInfo: any = { err: null, tipFn: noop };
let foundDc = false;
// found dc error
if (includeOne(shortArr, longArr)) {
const cbType: CbType = subFnInfo.desc ? cbTypes.MUTATE : cbTypes.WATCH;
dcErrorInfo = depKeyDcError(internal, fnCtx, changedDepKeys, cbType);
const dcErrorInfo = depKeyDcError(internal, fnCtx, changedDepKeys, cbType);
alertDepKeyDeadCycleErr(internal, dcErrorInfo);
fnCtx.dcErrorInfo = dcErrorInfo;
foundDc = true;
}
return dcErrorInfo;
return foundDc;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/helux-core/src/factory/creator/fake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ export const fakeDraftRoot = { val: null, isFake: true };
export const fakeMutateCtx = newMutateCtx({});

export const fakeReativeMeta: IReactiveMeta = {
isReactive: false,
isTop: true,
key: '',
fnKey: '',
sharedKey: 0,
moduleName: '',
depKeys: [],
writeKeys: [],
desc: '',
onRead: undefined,
Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/src/factory/creator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function buildSharedObject<T = any>(innerOptions: IInnerOptions, createOp
watchAndCallMutateDict({ target: sharedRoot, dict: parsedOptions.mutateFnDict });

// 创建顶层使用的响应式对象
const { draft, draftRoot } = buildReactive(internal, []);
const { draft, draftRoot } = buildReactive(internal, { isTop: true });
internal.reactive = draft;
internal.reactiveRoot = draftRoot;
clearInternal(parsedOptions.moduleName, internal.loc);
Expand Down
124 changes: 60 additions & 64 deletions packages/helux-core/src/factory/creator/mutateFn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { enureReturnArr, isPromise, noop, tryAlert } from '@helux/utils';
import { EVENT_NAME, FROM, SCOPE_TYPE } from '../../consts';
import { getRunningFn, getSafeFnCtx } from '../../factory/common/fnScope';
import { emitPluginEvent } from '../../factory/common/plugin';
import type { TInternal } from '../../factory/creator/buildInternal';
import { FN_DEP_KEYS, REACTIVE_META, TRIGGERED_WATCH } from '../../factory/creator/current';
import { alertDepKeyDeadCycleErr, analyzeErrLog, dcErr, inDeadCycle, probeDepKeyDeadCycle } from '../../factory/creator/deadCycle';
import { getStatusKey, setLoadStatus } from '../../factory/creator/loading';
Expand All @@ -12,7 +13,7 @@ import type { Fn, From, IInnerSetStateOptions, IWatchAndCallMutateDictOptions, M
import { createWatchLogic } from '../createWatch';
import { buildReactive, flushActive, innerFlush } from './reactive';

const noopAny: any = () => { };
const noopAny: any = () => {};

interface ICallMutateBase {
/** 透传给用户 */
Expand Down Expand Up @@ -47,7 +48,7 @@ export function callAsyncMutateFnLogic<T = SharedState>(
const { sharedKey } = internal;
const customOptions: IInnerSetStateOptions = { desc, sn, from };
const statusKey = getStatusKey(from, desc);
const { draft, draftRoot, meta } = buildReactive(internal, depKeys, { desc, from });
const { draft, draftRoot, meta } = buildReactive(internal, { depKeys, desc, from });
const flush = (desc: string) => {
innerFlush(sharedKey, desc);
};
Expand Down Expand Up @@ -153,32 +154,7 @@ export function callMutateFnLogic<T = SharedState>(targetState: T, options: ICal

const result = fn(...args);
finish(result, innerSetOptions);

// 存档一下收集到依赖,方便后续探测异步函数里的死循环可能存在的情况
if (isFirstCall && !fnItem.onlyDeps) {
const fnCtx = getRunningFn().fnCtx;
if (fnCtx) {
// 异步函数强制忽略依赖收集行为
fnItem.depKeys = markFnEnd();
} else {
// 可能在 notify 里已经执行过 markFnEnd 了,这里将 FN_DEP_KEYS.current 转移出来
fnItem.depKeys = FN_DEP_KEYS.current();
}
FN_DEP_KEYS.del();
}

const rmeta = REACTIVE_META.current();
// 当前 reactive 对象是在 fnCtx 内部调用时操作的,需探测死循环
// 形如 watch(()=>{ foo() }, ()=>[s.a]) function foo(){ reactiv.a+=1 }
if (rmeta.isReactive && rmeta.fnKey === fnItem.watchKey) {
const fnCtx = getSafeFnCtx(fnItem.watchKey);
const dcErrorInfo = probeDepKeyDeadCycle(internal, getSafeFnCtx(fnItem.watchKey), rmeta.writeKeys);
if (dcErrorInfo.err) {
fnCtx.dcErrorInfo = dcErrorInfo;
}
}

TRIGGERED_WATCH.del();
afterFnRun(internal, fnItem, isFirstCall);
return [internal.snap, null];
} catch (err: any) {
TRIGGERED_WATCH.del();
Expand All @@ -190,6 +166,52 @@ export function callMutateFnLogic<T = SharedState>(targetState: T, options: ICal
}
}

function afterFnRun(internal: TInternal, fnItem: MutateFnStdItem, isFirstCall: boolean) {
// 存档一下收集到依赖,方便后续探测异步函数里的死循环可能存在的情况
if (isFirstCall && !fnItem.onlyDeps) {
const fnCtx = getRunningFn().fnCtx;
if (fnCtx) {
// 异步函数强制忽略依赖收集行为
fnItem.depKeys = markFnEnd();
} else {
// 可能在 notify 里已经执行过 markFnEnd 了,这里将 FN_DEP_KEYS.current 转移出来
fnItem.depKeys = FN_DEP_KEYS.current();
}
FN_DEP_KEYS.del();
}
const rmeta = REACTIVE_META.current();
// 当前 reactive 对象是在 fnCtx 内部调用时操作的,需探测死循环
// 形如 watch(()=>{ foo() }, ()=>[s.a]) function foo(){ reactiv.a+=1 }
if (rmeta.isTop && rmeta.fnKey === fnItem.watchKey) {
// 发现死循环后,下一次执行被阻断
probeDepKeyDeadCycle(internal, getSafeFnCtx(fnItem.watchKey), rmeta.writeKeys);
}
// 标记 fnItem 所属 watch 运行结束
TRIGGERED_WATCH.del();
}

function initFnItem(internal: TInternal, fnItem: MutateFnStdItem) {
// clean
flushActive();
FN_DEP_KEYS.del();
markIgnore(false);

const fnCtx = getRunningFn().fnCtx;
if (fnCtx) {
// 将子函数信息挂上去
fnCtx.subFnInfo = fnItem;
// 优先读子函数配置,在读模块配置
fnCtx.checkDeadCycle = fnItem.checkDeadCycle ?? internal.checkDeadCycle;
// 双向记录一下 fnItem 和 watch 函数之间的关系
fnItem.watchKey = fnCtx.fnKey;
}

// 设定了依赖全部从 deps 函数获取,提前结束运行中的 fnCtx
if (fnItem.onlyDeps) {
fnItem.depKeys = markFnEnd();
}
}

/**
* 监听并运行 mutate 函数
* @param options
Expand All @@ -206,55 +228,29 @@ export function watchAndCallMutateDict(options: IWatchAndCallMutateDictOptions)
// 开始映射 mutate 函数相关数据依赖关系
createWatchLogic(
({ sn, isFirstCall }) => {
flushActive();
const { desc, fn, task, immediate, onlyDeps } = item;
const fnCtx = getRunningFn().fnCtx;
if (isFirstCall && fnCtx) {
// 将子函数信息挂上去
fnCtx.subFnInfo = item;
// 优先读子函数配置,在读模块配置
fnCtx.checkDeadCycle = item.checkDeadCycle ?? internal.checkDeadCycle;
// 双向记录一下 fnItem 和 watch 函数之间的关系
item.watchKey = fnCtx.fnKey;
// 设定了依赖全部从 deps 函数获取
if (onlyDeps) {
item.depKeys = markFnEnd();
}
if (isFirstCall) {
initFnItem(internal, item);
}

FN_DEP_KEYS.del();
const { desc, fn, task, immediate } = item;
const dc = inDeadCycle(usefulName, desc);

try {
// 已处于死循环中的函数,不再执行
if (dc.isIn) {
throw dcErr(usefulName, dc.cycle, desc);
}
const baseOpts = { sn, throwErr: true, isFirstCall, fnItem: item, from: FROM.MUTATE };
if (fn) {
// 包含 task 配置时,fn 只会在首次执行被调用一次
if (isFirstCall || !task) {
markIgnore(false);
callMutateFnLogic(target, baseOpts);
}
// 包含 task 配置时,fn 只会在首次执行被调用一次
if (fn && (isFirstCall || !task)) {
callMutateFnLogic(target, baseOpts);
}

// // 存档一下收集到依赖,方便后续探测异步函数里的死循环可能存在的情况
// if (isFirstCall) {
// if (fnCtx) {
// // 异步函数强制忽略依赖收集行为
// item.depKeys = markFnEnd();
// } else {
// // 可能在 notify 里已经执行过 markFnEnd 了,这里将 FN_DEP_KEYS.current 转移出来
// item.depKeys = FN_DEP_KEYS.current();
// }
// FN_DEP_KEYS.del();
// }

if (task) {
// 考虑到只有task立即执行task的情况,这里调用 markFnEnd 确保异步函数强制忽略依赖收集行为
isFirstCall && markFnEnd();
// 第一次调用时,如未显示定义 immediate 值,则触发规律是没有 fn 则执行 task,有 fn 则不执行 task
const canRunForFirstCall = isFirstCall && (immediate ?? !fn);
if (!isFirstCall || canRunForFirstCall) {
const canRunAtFirstCall = isFirstCall && (immediate ?? !fn);
if (!isFirstCall || canRunAtFirstCall) {
callAsyncMutateFnLogic(target, baseOpts);
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/src/factory/creator/operateState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function handleOperate(opParams: IOperateParams, opts: { internal: TInter
const { writeKeyPathInfo, ids, globalIds, writeKeys } = mutateCtx;
const writeKey = getDepKeyByPath(fullKeyPath, sharedKey);

if (currReactive.isReactive) {
if (currReactive.isTop) {
nodupPush(currReactive.writeKeys, writeKey);
}

Expand Down
14 changes: 9 additions & 5 deletions packages/helux-core/src/factory/creator/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function flush(sharedState: any, desc?: string) {
*/
export function flushActive() {
const rmeta = REACTIVE_META.current();
if (rmeta.isReactive) {
if (rmeta.isTop) {
innerFlush(rmeta.sharedKey, rmeta.desc);
REACTIVE_META.del(rmeta.key);
}
Expand Down Expand Up @@ -140,23 +140,27 @@ function markUsing(rKey: string) {
}

/**
* 创建全局使用的响应式共享对象
* 创建响应式共享对象
*/
export function buildReactive(internal: TInternal, fnDepKeys: string[], options?: { desc?: string; onRead?: OnOperate; from?: From }) {
export function buildReactive(
internal: TInternal,
options?: { isTop?: boolean; depKeys?: string[]; desc?: string; onRead?: OnOperate; from?: From },
) {
// 提供 draftRoot、draft,和 mutate、aciont 回调里对齐,方便用户使用 atom 时少一层 .val 操作
let draftRoot: any = {};
let draft: any = {};
const { rawState, deep, forAtom, isPrimitive, sharedKey, moduleName } = internal;
const { desc, onRead, from = FROM.REACTIVE } = options || {};
const { desc, onRead, from = FROM.REACTIVE, depKeys = [], isTop = false } = options || {};

const rKey = getReactiveKey();
const meta: IReactiveMeta = {
isReactive: true,
isTop,
moduleName,
key: rKey,
fnKey: '',
desc: desc || '',
sharedKey,
depKeys,
writeKeys: [],
onRead,
from,
Expand Down
54 changes: 16 additions & 38 deletions packages/helux-core/src/helpers/fnRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ interface IRnFnOpt {
/**
* 执行 watch 函数,内部会尝试检测死循环,防止无限调用情况产生
* TODO 后续优化拦截逻辑,提高可读性
* @param fnCtx
* @param options
* @returns
*/
function runWatch(fnCtx: IFnCtx, options: IRnFnOpt) {
const { isFirstCall = false, triggerReasons = [], sn = 0, from, internal = fakeInternal, desc } = options;
Expand All @@ -54,36 +51,25 @@ function runWatch(fnCtx: IFnCtx, options: IRnFnOpt) {
// 测试 OK,示例 MutateFnDc
// 检测出 mutate fn 里修改自己的依赖导致的死循环
// mutate({ fn: (draft)=> draft.a+=1 })
if (fnCtx.isRunning) {
const dcErrorInfo = probeDepKeyDeadCycle(internal, fnCtx, options.depKeys || []);
if (dcErrorInfo.err) {
fnCtx.dcErrorInfo = dcErrorInfo;
return;
}
if (fnCtx.isRunning && probeDepKeyDeadCycle(internal, fnCtx, options.depKeys || [])) {
return;
}

let rmeta = REACTIVE_META.current();
// 当前 reactive 对象是在 fnCtx 内部调用时操作的,需探测死循环
if (rmeta.fnKey === fnCtx.fnKey) {
const dcErrorInfo = probeDepKeyDeadCycle(internal, fnCtx, rmeta.writeKeys);
if (dcErrorInfo.err) {
fnCtx.dcErrorInfo = dcErrorInfo;
return;
}
} else {
// 提前 flush 可能已经存在的 reactive 对象,避免死循环误判
// 此操作会自动找到对应的函数执行,下面逻辑无需再执行
innerFlush(rmeta.sharedKey, rmeta.desc);
if (rmeta.fnKey === fnCtx.fnKey && probeDepKeyDeadCycle(internal, fnCtx, rmeta.writeKeys)) {
return;
}

// 提前 flush 可能已经存在的 reactive 对象,避免死循环误判
// 此操作会自动找到对应的函数执行,下面逻辑无需再执行
innerFlush(rmeta.sharedKey, rmeta.desc);
REACTIVE_META.del(rmeta.key);

const isReactiveInCb = fnCtx.isRunning === true && rmeta.isReactive;
const isReactiveInCb = fnCtx.isRunning === true && rmeta.isTop;
// 来自 watch(()=>{ r.a+=1 }, ()=>[s.a]) 的死循环
if (isReactiveInCb) {
const dcErrorInfo = probeDepKeyDeadCycle(internal, fnCtx, rmeta.writeKeys);
if (dcErrorInfo.err) {
fnCtx.dcErrorInfo = dcErrorInfo;
}
if (isReactiveInCb && probeDepKeyDeadCycle(internal, fnCtx, rmeta.writeKeys)) {
return;
}

fnCtx.isRunning = true;
Expand All @@ -96,24 +82,16 @@ function runWatch(fnCtx: IFnCtx, options: IRnFnOpt) {
// 来自以下类似示例的死循环
// 1 watch 对调用调用 watch(()=>{ r.a+=1 }, ()=>[s.a])
// 2 watch或mutate 中调用其他函数修改自身依赖 watch(()=>{ foo() }, ()=>[s.a]) function foo(){ reactiv.a+=1 }
if (rmeta.isReactive && rmeta.fnKey === fnCtx.fnKey) {
const dcErrorInfo = probeDepKeyDeadCycle(internal, fnCtx, rmeta.writeKeys);
if (dcErrorInfo.err) {
fnCtx.dcErrorInfo = dcErrorInfo;
return;
}
if (rmeta.isTop && rmeta.fnKey === fnCtx.fnKey && probeDepKeyDeadCycle(internal, fnCtx, rmeta.writeKeys)) {
return;
}

// 可能是一个异步执行的 task 任务,再次检查,注前面45行的 fnCtx.isRunning 在此种情况时会判断失效,这里需执行 fn 后做后置检查
// 形如 mutate({ deps:()=>[reative.a], async task(){ reative.a+=1 }, immediate: true })
if (ret && ret.task) {
if (rmeta.from === FROM.MUTATE) {
const dcErrorInfo = probeDepKeyDeadCycle(internal, fnCtx, options.depKeys || []);
// 发现异步 task 有死循环,则标记函数不可用
if (dcErrorInfo.err) {
fnCtx.dcErrorInfo = dcErrorInfo;
return;
}
// 发现异步 task 有死循环,则标记函数不可用
if (rmeta.from === FROM.MUTATE && probeDepKeyDeadCycle(internal, fnCtx, options.depKeys || [])) {
return;
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/src/helpers/insCtx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function attachInsProxyState(insCtx: InsCtxDef) {
};

if (isReactive) {
const { draft, draftRoot } = buildReactive(internal, [], { onRead: onOperate });
const { draft, draftRoot } = buildReactive(internal, { onRead: onOperate });
insCtx.proxyState = draftRoot;
insCtx.proxyStateVal = draft;
} else {
Expand Down
Loading

0 comments on commit a99d0c5

Please sign in to comment.