Migration guide for Lumino 1 to Lumino 2#
Iterables, iterators, and generators#
Overview and suggestions on iteration#
These are some guiding principles that came up over the course of refactoring the way we handle iteration in Lumino. They are not hard and fast rules, rather they are intuitions that arose while implementing the changes Lumino 2.
Iterate natively instead of using each(...)
#
Iterating through an iterable bar: Iterable<T>
using native for...of
, e.g., for (const foo of bar) {...}
is a better option than using each(bar, foo => ...)
.
All invocations of each(...)
have been removed in Lumino 2. See, for example, this commit.
Use [].forEach(...)
sparingly#
Now that we support native ES2018 iteration, for (const value of someArray) {...}
should be favored over someArray.forEach(...)
because it will not require a context shift every time it invokes the function being applied.
Use [Symbol.iterator]()
sparingly#
Unless you need a handle on multiple iterators simultaneously (e.g., the way zip(...)
is implemented in Lumino 2) or you need to hold on to multiple values of your iterable during iteration (e.g., the way we need both the first and the second value of an iterable to implement reduce(...)
in Lumino 2), most of the time you can simply use for...of
to iterate through any object that has a Symbol.iterator
method without invoking that method.
In many places where the Lumino iter()
utility function has been replaced in Lumino 2 it is not replaced with an invocation of the new Symbol.iterator
method.
Consider yield*
usage carefully#
If you have a method or function that returns an iterator, you might simply return values using the yield
keyword. If you are returning the contents of another iterable, you can use yield*
. However, if your logic depends on some predicate, remember that the predicate is checked when you iterate if you use yield*
. For example, consider the following:
const source = [1, 2, 3, 4, 5];
let flagged = false;
function* counter(): IterableIterator {
if (!flagged) {
yield* source;
}
}
// This is how a client would use the `counter()` function.
const iterable = counter();
flagged = true;
console.log(Array.from(iterable)); // []
Instead, if we modify the code:
import { empty } from '@lumino/algorithm';
const source = [1, 2, 3, 4, 5];
let flagged = false;
function counter(): IterableIterator<number> {
if (!flagged) {
return source[Symbol.iterator]();
}
return empty();
}
// This is how a client would use the `counter()` function.
const iterable = counter();
flagged = true;
console.log(Array.from(iterable)); // [1, 2, 3, 4. 5]
In these two examples, the client code that consumes counter()
is identical. But the results are different. Both implementations are correct, but depending on your use case, one may be more appropriate than the other. The important thing to consider is that the two implementations are not identical and yield different results.
Use Array.from(...)
sparingly#
toArray(...)
has been deprecated. You may be tempted to swap in Array.from(...)
when you update your code. This will work, but if you simply need to iterate through an iterable, you can use for...of
directly on the iterable object. This is more performant both in terms of CPU and memory than allocating and populating new Array
instance before iteration.
If you need a snapshot of every item in your iterable as an array, then Array.from(...)
is an appropriate replacement for toArray(...)
.
Look out for iterator.next() !== undefined
#
This pattern may appear in your code if you were using Lumino 1 iterators. Instead of checking iterator.next().done
, as is the case with native iterators, Lumino 1 iterators set next()
to the value undefined
as an optimization to prevent allocating an object with .value
and .done
, so when migrating from Lumino 1 to Lumino 2, this is a common cause of confusion. For example, a while
loop in Lumino 1 may have had this predicate:
while ((it = it.next()) !== undefined) {
// loop logic
}
The native (Lumino 2) version of this loop would be:
while (!(it = it.next()).done) {
// loop logic
}
Public API changes#
@lumino/algorithm
#
All of the iterator utilities have been changed to use native generators and iterators.
|
name |
note |
|
---|---|---|---|
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
We aren’t using this function anywhere |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Switch to generators |
❌ |
|
|
We aren’t using this class anywhere |
❌ |
|
|
Switch to |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Switch to |
❌ |
|
|
Previous implementation of |
☑️ |
|
|
|
☑️ |
|
|
|
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Support native types |
☑️ |
|
|
|
✅ |
|
|
Support native types |
✅ |
|
|
Reimplement with native types |
☑️ |
|
|
|
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Reimplement with native types |
☑️ |
|
|
|
✅ |
|
|
Reimplement with native types |
✅ |
|
|
Support native types |
✅ |
|
|
Reimplement with native types |
@lumino/collections
#
LinkedList
has been updated to accept native iterables and return native iterators.
|
name |
note |
|
---|---|---|---|
❌ |
|
|
Switch to |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Previous implementation of |
❌ |
|
|
Switch to |
✅ |
|
|
Accept |
✅ |
|
|
Accept |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |
@lumino/datagrid
#
DataGrid
selections are now native iterators.
|
name |
note |
|
---|---|---|---|
✅ |
|
|
Return |
✅ |
|
|
Return |
@lumino/disposable
#
Helper functions for DisposableSet
and ObservableDisposableSet
have been udpated.
|
name |
note |
|
---|---|---|---|
✅ |
|
|
Accept |
✅ |
|
|
Accept |
@lumino/dragdrop
#
|
name |
note |
|
---|---|---|---|
❌ |
|
|
Moved to |
❌ |
|
|
Moved to |
☑️ |
|
|
|
@lumino/widgets
#
Layout
and its sub-classes now use native iterators, e.g. implements Iterable<Widget>
.
|
name |
note |
|
---|---|---|---|
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
❌ |
|
|
Switch to |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |
✅ |
|
|
Return |