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

[New] ES2018+: Add CreateAsyncFromSyncIterator #105

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

ExE-Boss
Copy link
Contributor

Needed for #68 and implementing iterator‑helpers.


// eslint-disable-next-line no-shadow
var AsyncFromSyncIteratorPrototype = OrdinaryObjectCreate(AsyncIteratorPrototype);
CreateMethodProperty(AsyncFromSyncIteratorPrototype, 'next', function next(value) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Arguably, these function bodies should be wrapped in the callback to new $Promise.

2018/AsyncFromSyncIteratorContinuation.js Outdated Show resolved Hide resolved
2018/AsyncFromSyncIteratorContinuation.js Outdated Show resolved Hide resolved
2018/AsyncFromSyncIteratorContinuation.js Outdated Show resolved Hide resolved

// https://www.ecma-international.org/ecma-262/10.0/#sec-asyncfromsynciteratorcontinuation

module.exports = function AsyncFromSyncIteratorContinuation(result, promiseCapability) {
Copy link
Owner

Choose a reason for hiding this comment

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

the spec doesn't have the proper assertions here (see tc39/ecma262#2050) but i think we should write a helper and the runtime check here.

2018/AsyncFromSyncIteratorContinuation.js Outdated Show resolved Hide resolved
@ExE-Boss ExE-Boss force-pushed the feat/es2018/create-async-from-sync-iterator branch from a48d96e to a0eedfa Compare June 17, 2020 21:47
@ExE-Boss ExE-Boss requested a review from ljharb June 17, 2020 21:48
@codecov
Copy link

codecov bot commented Jun 17, 2020

Codecov Report

Merging #105 into master will decrease coverage by 0.58%.
The diff coverage is 47.61%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #105      +/-   ##
==========================================
- Coverage   90.89%   90.30%   -0.59%     
==========================================
  Files         670      673       +3     
  Lines        9444     9574     +130     
  Branches     2195     2199       +4     
==========================================
+ Hits         8584     8646      +62     
- Misses        860      928      +68     
Impacted Files Coverage Δ
es2018.js 100.00% <ø> (ø)
es2019.js 100.00% <ø> (ø)
2018/CreateAsyncFromSyncIterator.js 37.50% <37.50%> (ø)
2019/CreateAsyncFromSyncIterator.js 37.50% <37.50%> (ø)
helpers/AsyncFromSyncIterator.js 49.09% <49.09%> (ø)
GetIntrinsic.js 93.91% <0.00%> (-1.59%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4e8d479...a0eedfa. Read the comment docs.

throw new SyntaxError('This environment does not support Promises.');
}

var asyncIterator = new AsyncFromSyncIterator(syncIteratorRecord);
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
var asyncIterator = new AsyncFromSyncIterator(syncIteratorRecord);
var asyncIterator = AsyncFromSyncIterator(syncIteratorRecord);

i'm very confused why we'd want a constructor, or to encourage use of new.


// TODO: Use %AsyncIterator.from% once it's in ECMA-262

/** @type {(o: object, p: string | symbol, Desc: import('es-abstract').PropertyDescriptor) => boolean} */
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
/** @type {(o: object, p: string | symbol, Desc: import('es-abstract').PropertyDescriptor) => boolean} */

i'm not interested in jsdoc comments at this time


/** @type {(o: object, p: string | symbol, Desc: import('es-abstract').PropertyDescriptor) => boolean} */
var DefineOwnProperty = callBind(
require('./DefineOwnProperty'),
Copy link
Owner

Choose a reason for hiding this comment

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

this should be required at module level and used here

Comment on lines +24 to +26
function IsDataDescriptor(Desc) {
return !('[[Get]]' in Desc) && !('[[Set]]' in Desc);
},
Copy link
Owner

Choose a reason for hiding this comment

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

this should use require('./IsDataDescriptor').

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You do realise that this is now in the helpers directory, right?

As such, there is no IsDataDescriptor file that can be imported.

Copy link
Owner

Choose a reason for hiding this comment

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

In that case, it should take IsDataDescriptor as a function argument.

Comment on lines +27 to +36
GetIntrinsic('%Object.is%', true) || function SameValue(x, y) {
if (x === y) {
// 0 === -0, but they are not identical.
if (x === 0) {
return 1 / x === 1 / y;
}
return true;
}
return $isNaN(x) && $isNaN(y);
},
Copy link
Owner

Choose a reason for hiding this comment

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

this should just use require('./SameValue') directly (required at module level)

Copy link
Owner

Choose a reason for hiding this comment

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

This should also be passed as a function argument.

Comment on lines +37 to +58
function FromPropertyDescriptor(Desc) {
var obj = {};
if ('[[Value]]' in Desc) {
obj.value = Desc['[[Value]]'];
}
if ('[[Writable]]' in Desc) {
obj.writable = Desc['[[Writable]]'];
}
if ('[[Get]]' in Desc) {
obj.get = Desc['[[Get]]'];
}
if ('[[Set]]' in Desc) {
obj.set = Desc['[[Set]]'];
}
if ('[[Enumerable]]' in Desc) {
obj.enumerable = Desc['[[Enumerable]]'];
}
if ('[[Configurable]]' in Desc) {
obj.configurable = Desc['[[Configurable]]'];
}
return obj;
}
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
function FromPropertyDescriptor(Desc) {
var obj = {};
if ('[[Value]]' in Desc) {
obj.value = Desc['[[Value]]'];
}
if ('[[Writable]]' in Desc) {
obj.writable = Desc['[[Writable]]'];
}
if ('[[Get]]' in Desc) {
obj.get = Desc['[[Get]]'];
}
if ('[[Set]]' in Desc) {
obj.set = Desc['[[Set]]'];
}
if ('[[Enumerable]]' in Desc) {
obj.enumerable = Desc['[[Enumerable]]'];
}
if ('[[Configurable]]' in Desc) {
obj.configurable = Desc['[[Configurable]]'];
}
return obj;
}
FromPropertyDescriptor,

where FromPropertyDescriptor is require('./FromPropertyDescriptor') at module level

Comment on lines +61 to +68
var CreateMethodProperty = function CreateMethodProperty(O, P, V) {
return DefineOwnProperty(O, P, {
'[[Configurable]]': true,
'[[Enumerable]]': false,
'[[Writable]]': true,
'[[Value]]': V
});
};
Copy link
Owner

Choose a reason for hiding this comment

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

this should also be require('./CreateMethodProperty')

Comment on lines +70 to +72
var isObject = function (target) {
return target !== null && (typeof target === 'object' || typeof target === 'function');
};
Copy link
Owner

Choose a reason for hiding this comment

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

please use the inverse of helpers/isPrimitive for this.

Comment on lines +76 to +98
|| (function () {
var result = {};
if ($toStringTag) {
DefineOwnProperty(result, $toStringTag, {
'[[Writable]]': false,
'[[Enumerable]]': false,
'[[Configurable]]': true,
'[[Value]]': 'AsyncIterator'
});
}
if ($asyncIterator) {
CreateMethodProperty(
result,
$asyncIterator,
{
'[Symbol.asyncIterator]': function () {
return this;
}
}['[Symbol.asyncIterator]']
);
}
return result;
}());
Copy link
Owner

Choose a reason for hiding this comment

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

this should be moved to a separate helpers file, that's tested independently.

Comment on lines +103 to +220
undefined,
[{ value: unwrappedValue, done: done }]
);
},
promiseCapability['[[Reject]]']
);
};

var T = function T() {};
T.prototype = AsyncIteratorPrototype;
// eslint-disable-next-line no-shadow
var AsyncFromSyncIteratorPrototype = new T();

CreateMethodProperty(AsyncFromSyncIteratorPrototype, 'next', function next(value) {
// eslint-disable-next-line no-invalid-this
var O = this;
return new Promise(function (resolve, reject) {
internalSlot.assert(O, '[[SyncIteratorRecord]]');

var syncIteratorRecord = internalSlot.get(O, '[[SyncIteratorRecord]]');
var result = $apply(
syncIteratorRecord['[[NextMethod]]'],
syncIteratorRecord['[[Iterator]]'],
[value]
);

if (!isObject(result)) {
throw new $TypeError('iterator next must return an object');
}

return AsyncFromSyncIteratorContinuation(result, {
'[[Resolve]]': resolve,
'[[Reject]]': reject
});
});
});

CreateMethodProperty(AsyncFromSyncIteratorPrototype, 'return', {
'return': function (value) {
var O = this;
return new Promise(function (resolve, reject) {
internalSlot.assert(O, '[[SyncIteratorRecord]]');

var syncIterator = internalSlot.get(O, '[[SyncIteratorRecord]]')['[[Iterator]]'];
var returnMethod = syncIterator['return'];
if (returnMethod != null) {
if (!isCallable(returnMethod)) {
throw new $TypeError('iterator return is not a function');
}

return resolve({
value: value,
done: true
});
}

var result = $apply(returnMethod, syncIterator, [value]);
if (!isObject(result)) {
throw new $TypeError('iterator return must return an object');
}

return AsyncFromSyncIteratorContinuation(result, {
'[[Resolve]]': resolve,
'[[Reject]]': reject
});
});
}
}['return']);

CreateMethodProperty(AsyncFromSyncIteratorPrototype, 'throw', {
'throw': function (value) {
var O = this;
return new Promise(function (resolve, reject) {
internalSlot.assert(O, '[[SyncIteratorRecord]]');

var syncIterator = internalSlot.get(O, '[[SyncIteratorRecord]]')['[[Iterator]]'];
var throwMethod = syncIterator['return'];
if (throwMethod != null) {
if (!isCallable(throwMethod)) {
throw new $TypeError('iterator throw is not a function');
}
throw value;
}

var result = $apply(throwMethod, syncIterator, [value]);
if (!isObject(result)) {
throw new $TypeError('iterator throw must return an object');
}

return AsyncFromSyncIteratorContinuation(result, {
'[[Resolve]]': resolve,
'[[Reject]]': reject
});
});
}
}['throw']);

// eslint-disable-next-line consistent-return
return AsyncFromSyncIteratorPrototype;
}());
Copy link
Owner

Choose a reason for hiding this comment

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

as should this.

@ExE-Boss ExE-Boss marked this pull request as draft June 19, 2020 06:15
@ExE-Boss
Copy link
Contributor Author

I think a potentially better option might be to implement a AsyncFromSyncIterator helper in a separate package, and then have ES‑Abstract depend on it.

@ljharb
Copy link
Owner

ljharb commented Jun 19, 2020

Yeah, you're probably right - a separate package just for AsyncIterator probably makes sense, especially given the iterator helpers proposal.

I'll make these packages over the next few days and then update here.

@ExE-Boss
Copy link
Contributor Author

I have a work in progress implementation of the Iterator Helpers proposal in: @ExE‑Boss/Iterator‑Helpers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants