Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simpler implementation while still avoid stack overflow #5

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 9 additions & 29 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
const fromIter = iter => (start, sink) => {
if (start !== 0) return;
const iterator =
typeof Symbol !== 'undefined' && iter[Symbol.iterator]
? iter[Symbol.iterator]()
: iter;
let inloop = false;
let got1 = false;
let completed = false;
let res;
function loop() {
inloop = true;
while (got1 && !completed) {
got1 = false;
res = iterator.next();
if (res.done) {
sink(2);
break;
}
else sink(1, res.value);
}
inloop = false;
}
const isIterable = typeof Symbol !== 'undefined' && iter[Symbol.iterator];
const iterator = isIterable ? iter[Symbol.iterator]() : iter;
let disposed = false;
let result = {};
sink(0, t => {
if (completed) return

if (t === 1) {
got1 = true;
if (!inloop && !(res && res.done)) loop();
} else if (t === 2) {
completed = true;
}
if (disposed) return;
if (t === 1) result = iterator.next();
if (t === 2) disposed = true;
});
while (!result.done && !disposed) sink(1, result.value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might cause infinie loop. U assume in this implementation that u always receive pull immediately in response to pushing the value down

Copy link
Author

@franciscotln franciscotln Nov 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you give an example?
EDIT: you mean a sink calls the source with zero and then doesn't talk back with 1, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pipe(
  fromIter([1,2,3]),
  delay(100),
  forEach(v => console.log(v))
)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this example works normally. What you mentioned can occur just if for some reason a sink calls the source with 0 and doesn't immediately talkback with 1 or 2. That's true. I just wonder if that's a real scenario, I mean forEach, iterate, subscribe, all of them call source(0, (t, d) => { }) and in case of t === 0 || t === 1 there is an immediate talkback(1). Is that in compliance with the spec? I believe delays should happen through operators and not in the sink by not calling tb(1).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented sinks (like forEach, subscribe etc) indeed pull automatically - but this is not something that spec assumes. You could also implement backpressurePulls operator that could delay pulls sent upwards and this implementation wouldnt handle it well.

Also from what I see this implementation doesnt handle my delay example (I havent tested it though and I might be wrong). I think it will hang in the while loop, because it will try to send down the same value, rather than next ones.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it, it works fine if working with delay operators without patching the sinks we know.
In any case, I believe the original version that is implemented by Andre now should include these test cases you mentioned in test.js

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna clone the PR because it really doesnt support that a lazy sink that talks back after a delay, thanks @Andarist for the keen eye

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be a correct pullable implementation.

A pullable source should only push once for every pull (in a 1:1 ratio), not push everything at once.

if (!disposed) sink(2);
};

module.exports = fromIter;
27 changes: 16 additions & 11 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,31 @@ Convert an Iterable:

```js
const fromIter = require('callbag-from-iter');
const iterate = require('callbag-iterate');
const forEach = require('callbag-for-each');

const source = fromIter([10, 20, 30, 40]);

source(0, iterate(x => console.log(x)); // 10
// 20
// 30
// 40
forEach(x => console.log(x)(source); // 10
// 20
// 30
// 40
```

Convert an Iterator:

```js
const fromIter = require('callbag-from-iter');
const iterate = require('callbag-iterate');
const forEach = require('callbag-for-each');

const source = fromIter([10, 20, 30, 40].entries());
const source1 = fromIter([10, 20, 30, 40].entries());

iterate(x => console.log(x))(source); // [0,10]
// [1,20]
// [2,30]
// [3,40]
forEach(x => console.log(x))(source1); // [0,10]
// [1,20]
// [2,30]
// [3,40]

const source2 = fromIter(new Map().set('a', 1).set('b', 2));

forEach(x => console.log(x))(source2); // ['a', 1]
// ['b', 2]
```
6 changes: 3 additions & 3 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const test = require('tape');
const fromIter = require('./index');
const fromIter = require('.');

test('it sends array items (iterable) to a puller sink', t => {
t.plan(13);
Expand Down Expand Up @@ -69,7 +69,7 @@ test('it does not blow up the stack when iterating something huge', t => {
t.plan(2);
let i = 0;
function* gen() {
while (i < 1000000) {
while (i < 100000000) {
yield i++;
}
}
Expand All @@ -88,7 +88,7 @@ test('it does not blow up the stack when iterating something huge', t => {
return;
}
if (type === 2) {
t.equals(i, 1000000, '1 million items were iterated');
t.equals(i, 100000000, '100 million items were iterated');
iterated = true;
return;
}
Expand Down