Skip to content

Commit

Permalink
Merge pull request #3792 from tonyraoul/es2023-array-methods
Browse files Browse the repository at this point in the history
perf: ECMAScript2023 &  ECMAScript2022 array methods performance improvement for proxy
  • Loading branch information
mweststrate committed Nov 25, 2023
2 parents 1a0dd70 + 227290d commit a2db19e
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-trainers-beg.md
@@ -0,0 +1,5 @@
---
"mobx": minor
---

Improve observablearray proxy pefromance for es2023.array and es2022.array methods
4 changes: 2 additions & 2 deletions .github/workflows/build_and_test.yml
Expand Up @@ -15,10 +15,10 @@ jobs:
- name: Checkout Repo
uses: actions/checkout@master

- name: Setup Node.js 14.x
- name: Setup Node.js 20.x
uses: actions/setup-node@master
with:
node-version: 14.x
node-version: 20.x

- name: Install Dependencies
run: yarn --frozen-lockfile --ignore-scripts
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/coveralls.yml
Expand Up @@ -13,10 +13,10 @@ jobs:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0

- name: Setup Node.js 14.x
- name: Setup Node.js 20.x
uses: actions/setup-node@master
with:
node-version: 14.x
node-version: 20.x

- name: Install Dependencies
run: yarn --frozen-lockfile --ignore-scripts
Expand Down
35 changes: 35 additions & 0 deletions packages/mobx/__tests__/perf/perf.js
Expand Up @@ -196,6 +196,41 @@ results of this test:
t.end()
})

test(`${version} - array.es2023 findLastIndex methods`, function (t) {
gc()
let aCalc = 0
let bCalc = 0
const ar = observable([0])
const findLastIndexOfZero = computed(function () {
aCalc++
return ar.findLastIndex(x => x === 0);
})
const lastIndexOfZero = computed(function () {
bCalc++
return ar.lastIndexOf(0);
})
mobx.observe(findLastIndexOfZero, voidObserver, true)
mobx.observe(lastIndexOfZero, voidObserver, true)

const start = now()

t.equal(1, aCalc)
t.equal(1, bCalc)
for (let i = 1; i < 10000; i++) ar.push(i)

t.equal(0, lastIndexOfZero.get())
t.equal(0, findLastIndexOfZero.get())
t.equal(10000, aCalc)
t.equal(10000, bCalc)

const end = now()

log(
"Array findLastIndex loop - Updated in " + (end - start) + " ms."
)
t.end()
})

test(`${version} - array reduce`, function (t) {
gc()
let aCalc = 0
Expand Down
55 changes: 55 additions & 0 deletions packages/mobx/__tests__/v4/base/array.js
Expand Up @@ -154,6 +154,34 @@ test("find(findIndex) and remove", function () {
expect(a.remove(20)).toBe(false)
})

test("findLast(findLastIndex) and remove", function () {
const a = mobx.observable([10, 20, 20])
let idx = -1
function predicate(item, index) {
if (item === 20) {
idx = index
return true
}
return false
}
;[].findLastIndex;
expect(a.findLast(predicate)).toBe(20)
expect(a.findLastIndex(predicate)).toBe(2)
expect(a.findLast(predicate)).toBe(20)

expect(a.remove(20)).toBe(true)
expect(a.find(predicate)).toBe(20)
expect(idx).toBe(1)
expect(a.findIndex(predicate)).toBe(1)
idx = -1
expect(a.remove(20)).toBe(true)
expect(a.findLast(predicate)).toBe(undefined)
expect(idx).toBe(-1)
expect(a.findLastIndex(predicate)).toBe(-1)

expect(a.remove(20)).toBe(false)
})

test("concat should automatically slice observable arrays, #260", () => {
const a1 = mobx.observable([1, 2])
const a2 = mobx.observable([3, 4])
Expand Down Expand Up @@ -587,6 +615,8 @@ test("correct array should be passed to callbacks #2326", () => {
"filter",
"find",
"findIndex",
"findLast",
"findLastIndex",
"flatMap",
"forEach",
"map",
Expand Down Expand Up @@ -766,6 +796,31 @@ describe("dehances", () => {
expect([...array.values()]).toEqual([...dehanced.values()])
})

test("toReversed", () => {
expect(array.toReversed()).toEqual(dehanced.toReversed())
})

test("toSorted", () => {
expect(array.toSorted()).toEqual(dehanced.toSorted())
})

test("toSorted with args", () => {
expect(array.toSorted((a, b) => a - b)).toEqual(dehanced.toSorted((a, b) => a - b))
})

test("toSpliced", () => {
expect(array.toSpliced(1, 2)).toEqual(dehanced.toSpliced(1, 2))
})

test("with", () => {
expect(array.with(1, 5)).toEqual(dehanced.with(1, 5))
})

test("at", () => {
expect(array.at(1)).toEqual(dehanced.at(1))
expect(array.at(-1)).toEqual(dehanced.at(-1))
})

test("flat/flatMap", () => {
// not supported in V4
})
Expand Down
55 changes: 55 additions & 0 deletions packages/mobx/__tests__/v5/base/array.js
Expand Up @@ -178,6 +178,34 @@ test("find(findIndex) and remove", function () {
expect(a.remove(20)).toBe(false)
})

test("findLast(findLastIndex) and remove", function () {
const a = mobx.observable([10, 20, 20])
let idx = -1
function predicate(item, index) {
if (item === 20) {
idx = index
return true
}
return false
}
;[].findLastIndex;
expect(a.findLast(predicate)).toBe(20)
expect(a.findLastIndex(predicate)).toBe(2)
expect(a.findLast(predicate)).toBe(20)

expect(a.remove(20)).toBe(true)
expect(a.find(predicate)).toBe(20)
expect(idx).toBe(1)
expect(a.findIndex(predicate)).toBe(1)
idx = -1
expect(a.remove(20)).toBe(true)
expect(a.findLast(predicate)).toBe(undefined)
expect(idx).toBe(-1)
expect(a.findLastIndex(predicate)).toBe(-1)

expect(a.remove(20)).toBe(false)
})

test("concat should automatically slice observable arrays, #260", () => {
const a1 = mobx.observable([1, 2])
const a2 = mobx.observable([3, 4])
Expand Down Expand Up @@ -630,6 +658,8 @@ test("correct array should be passed to callbacks #2326", () => {
"filter",
"find",
"findIndex",
"findLast",
"findLastIndex",
"flatMap",
"forEach",
"map",
Expand Down Expand Up @@ -807,6 +837,31 @@ describe("dehances", () => {
expect([...array.values()]).toEqual([...dehanced.values()])
})

test("toReversed", () => {
expect(array.toReversed()).toEqual(dehanced.toReversed())
})

test("toSorted", () => {
expect(array.toSorted()).toEqual(dehanced.toSorted())
})

test("toSorted with args", () => {
expect(array.toSorted((a, b) => a - b)).toEqual(dehanced.toSorted((a, b) => a - b))
})

test("toSpliced", () => {
expect(array.toSpliced(1, 2)).toEqual(dehanced.toSpliced(1, 2))
})

test("with", () => {
expect(array.with(1, 5)).toEqual(dehanced.with(1, 5))
})

test("at", () => {
expect(array.at(1)).toEqual(dehanced.at(1))
expect(array.at(-1)).toEqual(dehanced.at(-1))
})

test("flat/flatMap", () => {
const nestedArray = [{ value: 1 }, [{ value: 2 }, [{ value: 3 }]]]
const dehancedNestedArray = nestedArray.map(dehancer)
Expand Down
7 changes: 7 additions & 0 deletions packages/mobx/src/types/observablearray.ts
Expand Up @@ -518,6 +518,7 @@ export var arrayExtensions = {
* Without this, everything works as well, but this works
* faster as everything works on unproxied values
*/
addArrayExtension("at", simpleFunc)
addArrayExtension("concat", simpleFunc)
addArrayExtension("flat", simpleFunc)
addArrayExtension("includes", simpleFunc)
Expand All @@ -527,15 +528,21 @@ addArrayExtension("lastIndexOf", simpleFunc)
addArrayExtension("slice", simpleFunc)
addArrayExtension("toString", simpleFunc)
addArrayExtension("toLocaleString", simpleFunc)
addArrayExtension("toSorted", simpleFunc)
addArrayExtension("toSpliced", simpleFunc)
addArrayExtension("with", simpleFunc)
// map
addArrayExtension("every", mapLikeFunc)
addArrayExtension("filter", mapLikeFunc)
addArrayExtension("find", mapLikeFunc)
addArrayExtension("findIndex", mapLikeFunc)
addArrayExtension("findLast", mapLikeFunc)
addArrayExtension("findLastIndex", mapLikeFunc)
addArrayExtension("flatMap", mapLikeFunc)
addArrayExtension("forEach", mapLikeFunc)
addArrayExtension("map", mapLikeFunc)
addArrayExtension("some", mapLikeFunc)
addArrayExtension("toReversed", mapLikeFunc)
// reduce
addArrayExtension("reduce", reduceLikeFunc)
addArrayExtension("reduceRight", reduceLikeFunc)
Expand Down

0 comments on commit a2db19e

Please sign in to comment.