Skip to content

Commit

Permalink
fix(pref-settimeout): cancel error
Browse files Browse the repository at this point in the history
  • Loading branch information
fupengl committed Jun 12, 2021
1 parent fad9c18 commit 2f136bb
Show file tree
Hide file tree
Showing 21 changed files with 164 additions and 59 deletions.
19 changes: 13 additions & 6 deletions __tests__/function.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,40 @@ describe('function', () => {
prefSetTimeout(() => {
endTime = Date.now();
}, timeout);
setTimeout(() => resolve(), timeout + 17);
setTimeout(() => resolve(), timeout + 16);
await promise;
expect(endTime - startTime).toBeLessThan(timeout + 16 * 2);
expect(endTime - startTime).toBeLessThan(timeout + 20);
});

// TODO check
test('pref-setInterval', async () => {
const prefRes: number[] = [];
const orgRes: number[] = [];
const interval = 200;
const max = 5;
const { promise, resolve } = defer();
let count = 0;
function done() {
if (count === 2) {
resolve();
}
}
const prefId = prefSetInterval(() => {
prefRes.push(Date.now());
console.log(prefRes);
if (prefRes.length >= max) {
clearPrefSetInterval(prefId);
count++;
done();
}
}, interval);
const id = setInterval(() => {
orgRes.push(Date.now());
if (orgRes.length >= max) {
clearInterval(id);
resolve();
count++;
done();
}
}, interval);
await promise;
expect(prefRes).toEqual(orgRes);
expect(prefRes.every((v, i) => Math.abs(orgRes[i] - v) < 16 * 2)).toBe(true);
});
});
14 changes: 7 additions & 7 deletions src/array/array-move.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/**
* 移动数组中元素位置
* @param arr
* @param array
* @param from
* @param to
*/
function arrayMove<T>(arr: T[], from: number, to: number) {
const startIndex = from < 0 ? arr.length + from : from;
function arrayMove<T>(array: T[], from: number, to: number) {
const startIndex = from < 0 ? array.length + from : from;

if (startIndex >= 0 && startIndex < arr.length) {
const endIndex = to < 0 ? arr.length + to : to;
if (startIndex >= 0 && startIndex < array.length) {
const endIndex = to < 0 ? array.length + to : to;

const [item] = arr.splice(from, 1);
arr.splice(endIndex, 0, item);
const [item] = array.splice(from, 1);
array.splice(endIndex, 0, item);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/array/array-random.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* 从数组中随机获取一项
* @param arr {T[]}
* @param array {T[]}
* @return {T}
*/
function arrayRandom<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
function arrayRandom<T>(array: T[]): T {
return array[Math.floor(Math.random() * array.length)];
}

export default arrayRandom;
16 changes: 16 additions & 0 deletions src/array/array-splice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import arrayEachRight from './each-right';

/**
* array splice 多个index
* @param array
* @param indexes
*/
function arraySplice<T>(array: T[], ...indexes: number[]) {
arrayEachRight(array, (_, index) => {
if (indexes.includes(index)) {
array.splice(index, 1);
}
});
}

export default arraySplice;
6 changes: 3 additions & 3 deletions src/array/array-to-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import type { Dictionary, PropertyPath } from '../type';

/**
* 对象数组,指定值作为map的key
* @param arr {T[]}
* @param array {T[]}
* @param paths {keyof T}
* @return {{ [string] : T }}
*/
function arrayToMap<T extends Dictionary<any>>(arr: T[], paths: PropertyPath[]) {
return arr.reduce<Record<string, T>>((acc, item) => {
function arrayToMap<T extends Dictionary<any>>(array: T[], paths: PropertyPath[]) {
return array.reduce<Record<string, T>>((acc, item) => {
acc[get(item, paths)!] = { ...item };
return acc;
}, {});
Expand Down
6 changes: 3 additions & 3 deletions src/array/array-to-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type TreeNode<T, K extends string = 'children'> = T & Partial<Record<K, TreeNode

/**
* 数组通过关联主键,返回树
* @param arr
* @param array
* @param opts
* @return tree { { [id]: TreeNode } } 可以根据root节点id获取树
* @example
Expand Down Expand Up @@ -66,10 +66,10 @@ function arrayToTree<
K extends string,
Opts extends ArrayToTreeOpts<T, K> = ArrayToTreeOpts<T, K>,
Node = TreeNode<T, NonNullable<Opts['childrenKey']>>
>(arr: T[], opts?: Opts): Record<string, Node> {
>(array: T[], opts?: Opts): Record<string, Node> {
const { parentPrimaryKey = 'parentId', primaryKey = 'id', childrenKey = 'children' } = opts || {};
const hasOwnProperty = Object.prototype.hasOwnProperty;
return arr.reduce<Record<string, Node>>((branches, node) => {
return array.reduce<Record<string, Node>>((branches, node) => {
const parentId = node[parentPrimaryKey];
const itemId = node[primaryKey];
if (!hasOwnProperty.call(branches, parentId)) {
Expand Down
8 changes: 4 additions & 4 deletions src/array/chunk.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* 将数组分成指定大小的较小数组
* @param arr
* @param array
* @param size default 1
*/
function chunk<T>(arr: Array<T>, size: number = 1): T[][] {
return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
arr.slice(i * size, i * size + size),
function chunk<T>(array: Array<T>, size: number = 1): T[][] {
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
array.slice(i * size, i * size + size),
);
}

Expand Down
20 changes: 20 additions & 0 deletions src/array/each-right.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* 从右边foreach数组
* @param array
* @param iteratee
*/
function arrayEachRight<T>(
array: T[],
iteratee: (item: T, index: number, array: T[]) => false | void,
) {
let length = array == null ? 0 : array.length;

while (length--) {
if (iteratee(array[length], length, array) === false) {
break;
}
}
return array;
}

export default arrayEachRight;
2 changes: 2 additions & 0 deletions src/array/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export { default as arrayRandom } from './array-random';
export { default as arrayToTree } from './array-to-tree';
export type { ArrayToTreeOpts } from './array-to-tree';
export { default as arrayMove } from './array-move';
export { default as arrayEachRight } from './each-right';
export { default as arraySplice } from './array-splice';
8 changes: 4 additions & 4 deletions src/array/map-pick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Dictionary, PropertyPath } from '../type';

/**
* 从数组对象中获取指定的值,返回新的数组
* @param arr {Array<Object>>}
* @param array {Array<Object>>}
* @param keyPaths {<key, PropertyPath>} key 返回对象key, path 对象中值的key
* @param keyPredicates {<key, predicate>} key 返回对象key, predicate 返回需要的值
* @example
Expand All @@ -21,19 +21,19 @@ function mapPick<
Value = T[keyof T],
Item = Record<K, Value>
>(
arr: readonly T[],
array: readonly T[],
keyPaths?: Record<K, (PropertyPath | keyof T)[]>,
keyPredicates?: Record<K, (key: K, value: T, index: number, array: readonly T[]) => Value>,
): Item[] {
const resultKeys = Object.keys(keyPaths || keyPredicates || {});
if (!resultKeys.length) {
return [];
}
return arr.reduce<Item[]>((result, item, index) => {
return array.reduce<Item[]>((result, item, index) => {
result.push(
resultKeys.reduce<Item>((acc, key) => {
if (keyPredicates && isFunction(keyPredicates[key])) {
acc[key] = keyPredicates[key].call(global, key, item, index, arr);
acc[key] = keyPredicates[key].call(global, key, item, index, array);
} else if (keyPaths && keyPaths[key]) {
acc[key] = get(item, keyPaths[key]);
}
Expand Down
6 changes: 3 additions & 3 deletions src/array/shuffle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* 随机排列数组
* @param a {array} 需要打乱顺序的数组
* @param array {array} 需要打乱顺序的数组
*/
function shuffle<T = any>(a: T[]): T[] {
const t = a.slice();
function shuffle<T = any>(array: T[]): T[] {
const t = array.slice();
for (let i = t.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[t[i], t[j]] = [t[j], t[i]];
Expand Down
1 change: 1 addition & 0 deletions src/function/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export { prefSetTimeout, clearPrefTimeout } from './pref-setTimeout';
export { prefSetInterval, clearPrefSetInterval } from './pref-setInterval';
export { default as debounce } from './debounce';
export { default as throttle } from './throttle';
export { default as once } from './once';
12 changes: 12 additions & 0 deletions src/function/once.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { AnyFn } from '../type';
import before from './before';

/**
* 只执行一次,之后调用返回第一次的结果
* @param func
*/
function once<FN extends AnyFn>(func: FN) {
return before<FN>(2, func);
}

export default once;
32 changes: 9 additions & 23 deletions src/function/pref-setInterval.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,28 @@
import requestAnimationFrame from '../bom/requestAnimationFrame';
import cancelAnimationFrame from '../bom/cancelAnimationFrame';
import incrementId from './increment-id';

const id = incrementId();

const timerIdMap: Record<number, number> = {};
import { prefSetTimeout, clearPrefTimeout } from './pref-setTimeout';

/**
* 优先使用 requestAnimationFrame 实现 setInterval
* 如果没有 requestAnimationFrame 则用 setTimeout 实现 setInterval
* @note 当窗口未激活的时候会暂停
* @param handler
* @param ms
* @param args
*/
export function prefSetInterval(handler: Function, ms?: number, ...args: any[]): number {
const _id = id();
const interval = ms || 0;
let startTime = Date.now();
let endTime = startTime;
const loop = () => {
timerIdMap[_id] = requestAnimationFrame(loop);
endTime = Date.now();
if (endTime - startTime >= interval) {
function loop() {
return prefSetTimeout(() => {
handler(...args);
startTime = endTime;
}
};
timerIdMap[_id] = requestAnimationFrame(loop);
return _id;
loop();
}, interval);
}
return loop();
}

/**
* 取消 prefSetInterval
* @param handle
*/
export function clearPrefSetInterval(handle: number) {
if (timerIdMap[handle]) {
cancelAnimationFrame(timerIdMap[handle]);
delete timerIdMap[handle];
}
clearPrefTimeout(handle);
}
12 changes: 11 additions & 1 deletion src/function/pref-setTimeout.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import requestAnimationFrame from '../bom/requestAnimationFrame';
import cancelAnimationFrame from '../bom/cancelAnimationFrame';
import isNative from '../is/is-native';
import incrementId from './increment-id';

const id = incrementId();

const timerIdMap: Record<number, number> = {};

const useRAF = isNative(requestAnimationFrame);

/**
* 优先使用 requestAnimationFrame 实现 setTimeout
* 如果没有 requestAnimationFrame 则使用 native setTimeout
* @note 当窗口未激活的时候会暂停
* @param handler
* @param timeout
* @param args
*/
export function prefSetTimeout(handler: Function, timeout?: number, ...args: any[]): number {
if (!useRAF) {
return setTimeout(handler, timeout);
}
const _id = id();
const interval = timeout || 0;
const startTime = Date.now();
Expand All @@ -22,7 +29,7 @@ export function prefSetTimeout(handler: Function, timeout?: number, ...args: any
timerIdMap[_id] = requestAnimationFrame(loop);
endTime = Date.now();
if (endTime - startTime >= interval) {
clearPrefTimeout(timerIdMap[_id]);
clearPrefTimeout(_id);
handler(...args);
}
};
Expand All @@ -35,6 +42,9 @@ export function prefSetTimeout(handler: Function, timeout?: number, ...args: any
* @param handle
*/
export function clearPrefTimeout(handle: number) {
if (!useRAF) {
return clearTimeout(handle);
}
if (timerIdMap[handle]) {
cancelAnimationFrame(timerIdMap[handle]);
delete timerIdMap[handle];
Expand Down
5 changes: 3 additions & 2 deletions src/is/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export { default as isError } from './is-Error';
export { default as isNaN } from './is-NaN';
export { default as isNil } from './is-Nil';
export { default as isNull } from './is-Null';
export { default as isDef } from './is-Def';
export { default as isDef } from './is-def';
export { default as isUndefined, isUndefinedStr } from './is-Undefined';
export { default as isString } from './is-String';
export { default as isNumber } from './is-Number';
Expand All @@ -26,5 +26,6 @@ export { default as isSymbol } from './is-Symbol';
export { default as isWindow } from './is-Window';
export { default as isFile } from './is-File';
export { default as isPrototype } from './is-Prototype';
export { default as isDigit } from './is-Digit';
export { default as isDigit } from './is-digit';
export { default as isHTMLElement } from './is-HTMLElement';
export { default as isNative } from './is-native';
File renamed without changes.
File renamed without changes.
29 changes: 29 additions & 0 deletions src/is/is-native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import isFunction from './is-Function';
import isObject from './is-Object';

/**
* 判断是否为平台原生对象
* @link https://davidwalsh.name/detect-native-function
* @example
*
* isNative(window) // true
* isNative(alert) // true
*/
function isNative(value) {
const toString = Object.prototype.toString;
const fnToString = Function.prototype.toString;
const regxHostCtor = /^[object .+?Constructor]$/;
const reNative = RegExp(
'^' +
String(toString)
.replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&')
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') +
'$',
);

return isFunction(value)
? reNative.test(fnToString.call(value))
: (value && isObject(value) && regxHostCtor.test(toString.call(value))) || false;
}

export default isNative;
Loading

0 comments on commit 2f136bb

Please sign in to comment.