Symbol.await proposal
> > > Hi James, > > I did alot of upgrading of javascript codebase to typescript code base > about 2 years ago for a year, where the goal was to get everything promised > and then basically > were possible ideally move to the await method. This required for us to > come up with peace meal approach were our codebase would have all forms and > for legacy integration of new code would have also gone back from a promise > to the callback. > I know we had a lot of fun and games with all different promise versions > and interface incompatibilities. At times was good enough to just > Promise.resolve(..promise) because that solve is matched in promise > BlueBird and ts promise implementations and things. > > What I can say is if you implement a wrapper function, whereby if the call > back is empty you return a promise and the type system typically would help > you out here. > > > https://github.com/wesleyolis/mongooseRelationalTypes/blob/master/src/mongoose.ts > > > > exec<TCallBack extends ((err: Error, res: QueryResultsDocumentModel< > TModelParts, Populate, DeepPopulate, ArrayOfResults, Primative, Lean>) => > void) | undefined> (callback: TCallBack): undefined extends TCallBack ? > Promise<QueryResultsDocumentModel<TModelParts, Populate, DeepPopulate, > ArrayOfResults, Primative, Lean>> : void; I also had to write my own > promise implementation, because we had some non-conformant callbacks, which > in which I had to get some results out on execution and not on the async > callback result. In this promise implementation I also typically, did some > optimizations, which depended on how many parameters one would pass in and > also the ordering of the terms because I wanted to make it more object > orientation, approach. The one thing which is not in here, which I wanted > is for the ability but I had push back is for if there where not additional > parameters to execute immediately, which is kinda not the best, because of > bit iffi. The other would have been that execute automatically, by you > passing in the additional parameters as auxiliary parameters after the > first two or one. const results = await Promisify(method)(param1, param2, > param3, param3); I get the fact that by calling promisify on a function > and that it then stores it in the parent scope (this) symbol, would save > overhead when having to do it alot. As the resulting function is > transformation is cached, however, one needs to remember that function name > is key in the prototype or instance method. So basically the key would be > the function name, then below that you would have two have another two > keys, one for the async symbol and one for the normal function. So either > the function name in my mind needs be suffix or prefix with symbol > convention or need to figure out how introduce some other mechanism. Is > possible mabye to overload await implementation, such that it can check for > the existence of Symbol, if it exists then it knows to call async version. > if it doesn't then it be call existing one. As of current the await, will > return the promise results or just the result of a function call. So you > could just replace that function entirely at the instance level too. Also > look at using promiseALL and doing shallow copies and then doing > promisifyALL on the shallow copy. > function promisify (test) { > > return async function() { > console.log('test'); > return test > } > } > > myFunction = promisify(myFunction); > > (async () => console.log(await myFunction('a','b')))(); > > https://github.com/wesleyolis/BlueBirdObjectOrientatedPromisification/blob/master/src/BlueBirdPromisification.ts With > only a callback will execute the promise and return the result > > const result = await Promisify(method) > > > <https://github.com/wesleyolis/BlueBirdObjectOrientatedPromisification#with-mutiple-parameters-will-return-a-promisified-method>With > mutiple parameters will return a promisified method > > const promise = Promisify(method);const results = await promise(param1, param2, param3, param3); > > > <https://github.com/wesleyolis/BlueBirdObjectOrientatedPromisification#object-and-method-instead-of-using-bluebird-context>Object > and Method, instead of using bluebird context > > Will ensure that the method key exists on the object. > > <https://github.com/wesleyolis/BlueBirdObjectOrientatedPromisification#with-only-a-callback-will-execute-the-promise-and-return-the-result-1>With > only a callback will execute the promise and return the result > > const result = await Promisify(object, 'method'); > > > <https://github.com/wesleyolis/BlueBirdObjectOrientatedPromisification#with-mutiple-parameters-will-return-a-promisified-method-1>With > mutiple parameters will return a promisified method > > const promise = Promisify(object, 'method');const results = await promise(param1, param2, param3, param3); > > > <https://github.com/wesleyolis/BlueBirdObjectOrientatedPromisification#promises-return-type-can-also-be-overloaded>Promises > Return type can also be overloaded > > Requires spesifying both Promise Return type and callback method type. > > const promise = Promisify<{results : ''},(() => void)>(method); > > > <https://github.com/wesleyolis/BlueBirdObjectOrientatedPromisification#promise-with-returntype-some-weird-libaries>Promise > with ReturnType, some weird libaries. > > const promise = PromisifyReturn(...);let returnType = {}promise(....,returnType); > console.log(returnType['executeResult']); > > > On Mon, Jun 22, 2020 at 9:21 PM James M Snell <jasnell at gmail.com> wrote: > >> For many legacy code bases that are based on callbacks mechanisms like >> node.js' promisify function are required to help facilitate the transition >> from callback centric code to Promise-centric. A lot of the time, these can >> follow straightforward rules without requiring customization. However, at >> other times it is necessary for user code to provide custom implementations >> of the Promise-version of the function. >> >> In Node.js, we accomplish this by allowing a function to have a symbol >> attached whose value is an alternative function that is returned by the >> promisify function >> >> For instance, >> >> function myFunction(foo, bar, callback) { >> callback(null, foo, bar); >> } >> myFunction[util.customPromisifySymbol] = async function(foo, bar) { >> return [foo, bar]; >> } >> >> const { promisify } = require('util'); >> const mine = promisify(myFunction); >> (async () => console.log(await mine('a','b')))(); >> >> As a convenience built into the language, it would be nice to be able to >> short-circuit the need to call promisify with a special language-level >> Symbol used specifically for this purpose: >> >> function myFunction(foo, bar, callback) { >> callback(null, foo, bar); >> } >> myFunction[Symbol.await] = async function(foo, bar) { >> return [foo, bar]; >> } >> >> (async () => console.log(await myFunction('a','b')))(); >> >> The idea here is that if the function being awaited has the >> [Symbol.await] property whose value is a function, then that function is >> called when the await keyword is used. That is, >> >> myFunction('a', 'b', callback); // Invokes myFunction directly >> await myFunction('a', 'b'); // Invokes myFunction[Symbol.await] >> >> if the Symbol.await property is not set or is not callable, then it would >> fallback to default behavior. >> >> Automatic handling of this binding should also happen but has some >> technical detail to work out: >> >> const obj = { >> a: 1, >> foo() {} >> }; >> obj.foo[Symbol.await] = async function() { >> return this.a; >> } >> await obj.foo(); // Calls await obj.foo[Symbol.await] with bound this >> >> This approach would make it far easier for legacy code bases to make the >> transition to async/await syntax while maintaining legacy compat. >> >> Before writing up a formal proposal, I wanted to solicit some feedback on >> this approach to see what folks thought. >> >> - James >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > > > -- > ---- > GitHub:https://github.com/wesleyolis > LinkedIn:https://www.linkedin.com/in/wesley-walter-anton-oliver-85466613b/ > Blog/Website:https://sites.google.com/site/wiprogamming/Home > Skype: wezley_oliver > MSN messenger: wesley.olis at gmail.com > -- ---- GitHub:https://github.com/wesleyolis LinkedIn:https://www.linkedin.com/in/wesley-walter-anton-oliver-85466613b/ Blog/Website:https://sites.google.com/site/wiprogamming/Home Skype: wezley_oliver MSN messenger: wesley.olis at gmail.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20200622/9a79a59b/attachment-0001.html>
One of the common refactorings I do is:
let file1 = await readFile(filename1) let file2 = await readFile(filename2) // to let [file1, file2] = await Promise.all([ readFile(filename1), readFile(filename2), ])
I would be very confused if refactoring it in this way made my code break
because of some implicit behavior around await
specifically.
I have seen some APIs that switch to promises when a callback argument is not provided. Is this not a sufficient solution?
One of the common refactorings I do is: let file1 = await readFile(filename1) let file2 = await readFile(filename2) // to let [file1, file2] = await Promise.all([ readFile(filename1), readFile(filename2), ]) I would be very confused if refactoring it in this way made my code break because of some implicit behavior around `await` specifically. I have seen some APIs that switch to promises when a callback argument is not provided. Is this not a sufficient solution? On Mon, Jun 22, 2020 at 12:22 PM James M Snell <jasnell at gmail.com> wrote: > For many legacy code bases that are based on callbacks mechanisms like > node.js' promisify function are required to help facilitate the transition > from callback centric code to Promise-centric. A lot of the time, these can > follow straightforward rules without requiring customization. However, at > other times it is necessary for user code to provide custom implementations > of the Promise-version of the function. > > In Node.js, we accomplish this by allowing a function to have a symbol > attached whose value is an alternative function that is returned by the > promisify function > > For instance, > > function myFunction(foo, bar, callback) { > callback(null, foo, bar); > } > myFunction[util.customPromisifySymbol] = async function(foo, bar) { > return [foo, bar]; > } > > const { promisify } = require('util'); > const mine = promisify(myFunction); > (async () => console.log(await mine('a','b')))(); > > As a convenience built into the language, it would be nice to be able to > short-circuit the need to call promisify with a special language-level > Symbol used specifically for this purpose: > > function myFunction(foo, bar, callback) { > callback(null, foo, bar); > } > myFunction[Symbol.await] = async function(foo, bar) { > return [foo, bar]; > } > > (async () => console.log(await myFunction('a','b')))(); > > The idea here is that if the function being awaited has the [Symbol.await] > property whose value is a function, then that function is called when the > await keyword is used. That is, > > myFunction('a', 'b', callback); // Invokes myFunction directly > await myFunction('a', 'b'); // Invokes myFunction[Symbol.await] > > if the Symbol.await property is not set or is not callable, then it would > fallback to default behavior. > > Automatic handling of this binding should also happen but has some > technical detail to work out: > > const obj = { > a: 1, > foo() {} > }; > obj.foo[Symbol.await] = async function() { > return this.a; > } > await obj.foo(); // Calls await obj.foo[Symbol.await] with bound this > > This approach would make it far easier for legacy code bases to make the > transition to async/await syntax while maintaining legacy compat. > > Before writing up a formal proposal, I wanted to solicit some feedback on > this approach to see what folks thought. > > - James > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20200622/6ce045c7/attachment.html>
The one interesting case you have to look at dealing with, is the one I came across where by the call of a function can return a results and will then later async call the callback in the future. Because with the proposal above, you have lost the ability to, return the return result and callback results you have return {execReturn:..., awaitResult:}, this doesn't happen offen.
I think one approach that you could take but wouldn't always work, check for additional the call back parameter, then either return Promisification or you do the call back execution. a wrapper would help with that.
The other could also look at a special import syntax for modules, whereby methods are automatically promisified, with this wrap approach or shallow copy.
The problem is that for a function that can accept in javascript any number of parameters, arguments, were is no finite number of arguments, you can't be certain that there is a callback or if the last parameter is indeed a param. One could do type check on the last parameter, otherwise you have a naming convention for functions, to avoid the confusion, provided you could check the key(name) Promise all suffers the same problem with the last parameter, it is a convention.
Kind ,
Wesley Oliver
Hi, The one interesting case you have to look at dealing with, is the one I came across where by the call of a function can return a results and will then later async call the callback in the future. Because with the proposal above, you have lost the ability to, return the return result and callback results you have return {execReturn:..., awaitResult:}, this doesn't happen offen. I think one approach that you could take but wouldn't always work, check for additional the call back parameter, then either return Promisification or you do the call back execution. a wrapper would help with that. The other could also look at a special import syntax for modules, whereby methods are automatically promisified, with this wrap approach or shallow copy. The problem is that for a function that can accept in javascript any number of parameters, arguments, were is no finite number of arguments, you can't be certain that there is a callback or if the last parameter is indeed a param. One could do type check on the last parameter, otherwise you have a naming convention for functions, to avoid the confusion, provided you could check the key(name) Promise all suffers the same problem with the last parameter, it is a convention. Kind Regards, Wesley Oliver On Mon, Jun 22, 2020 at 9:21 PM James M Snell <jasnell at gmail.com> wrote: > For many legacy code bases that are based on callbacks mechanisms like > node.js' promisify function are required to help facilitate the transition > from callback centric code to Promise-centric. A lot of the time, these can > follow straightforward rules without requiring customization. However, at > other times it is necessary for user code to provide custom implementations > of the Promise-version of the function. > > In Node.js, we accomplish this by allowing a function to have a symbol > attached whose value is an alternative function that is returned by the > promisify function > > For instance, > > function myFunction(foo, bar, callback) { > callback(null, foo, bar); > } > myFunction[util.customPromisifySymbol] = async function(foo, bar) { > return [foo, bar]; > } > > const { promisify } = require('util'); > const mine = promisify(myFunction); > (async () => console.log(await mine('a','b')))(); > > As a convenience built into the language, it would be nice to be able to > short-circuit the need to call promisify with a special language-level > Symbol used specifically for this purpose: > > function myFunction(foo, bar, callback) { > callback(null, foo, bar); > } > myFunction[Symbol.await] = async function(foo, bar) { > return [foo, bar]; > } > > (async () => console.log(await myFunction('a','b')))(); > > The idea here is that if the function being awaited has the [Symbol.await] > property whose value is a function, then that function is called when the > await keyword is used. That is, > > myFunction('a', 'b', callback); // Invokes myFunction directly > await myFunction('a', 'b'); // Invokes myFunction[Symbol.await] > > if the Symbol.await property is not set or is not callable, then it would > fallback to default behavior. > > Automatic handling of this binding should also happen but has some > technical detail to work out: > > const obj = { > a: 1, > foo() {} > }; > obj.foo[Symbol.await] = async function() { > return this.a; > } > await obj.foo(); // Calls await obj.foo[Symbol.await] with bound this > > This approach would make it far easier for legacy code bases to make the > transition to async/await syntax while maintaining legacy compat. > > Before writing up a formal proposal, I wanted to solicit some feedback on > this approach to see what folks thought. > > - James > _______________________________________________ > es-discuss mailing list > es-discuss at mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > -- ---- GitHub:https://github.com/wesleyolis LinkedIn:https://www.linkedin.com/in/wesley-walter-anton-oliver-85466613b/ Blog/Website:https://sites.google.com/site/wiprogamming/Home Skype: wezley_oliver MSN messenger: wesley.olis at gmail.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20200622/746f8138/attachment-0001.html>
> Hi, > > That doesn't work, kinda work in javascript world where the function has > finite, fixed set of arguments, were there is a definition of last one. > Typescript helps a lot with that, because > it can ensure to a higher degree, that types are correct, js on its own, > loose so you have less guarantees of the binding. > Just have to assume that every function has like a callback by convention, > if it doesn't then break things again. > Typescript does help. > > function anyArguments(callback) { > let args = [...arguments]; > // args.length > 99 and callback is the 1st parameters. > } > > You can check out this is want project setup, that allow us to peace meal > upgrade to ts. > https://github.com/wesleyolis/configFactoryLoaderAndValidator > Few additional pointers: > type AnyJS = any; / this allow you keep track of intentional usage, so > search codebase later. > If you have old library with no types, then basically import the new > library with types under a different alias name, > then have both old and new versions and peace meal upgrade as required. > > Kind Regards, > > Wesley Oliver > > > > On Mon, Jun 22, 2020 at 10:13 PM Jamie <me at thejameskyle.com> wrote: > >> One of the common refactorings I do is: >> >> let file1 = await readFile(filename1) >> let file2 = await readFile(filename2) >> // to >> let [file1, file2] = await Promise.all([ >> readFile(filename1), >> readFile(filename2), >> ]) >> >> I would be very confused if refactoring it in this way made my code break >> because of some implicit behavior around `await` specifically. >> >> I have seen some APIs that switch to promises when a callback argument is >> not provided. Is this not a sufficient solution? >> >> On Mon, Jun 22, 2020 at 12:22 PM James M Snell <jasnell at gmail.com> wrote: >> >>> For many legacy code bases that are based on callbacks mechanisms like >>> node.js' promisify function are required to help facilitate the transition >>> from callback centric code to Promise-centric. A lot of the time, these can >>> follow straightforward rules without requiring customization. However, at >>> other times it is necessary for user code to provide custom implementations >>> of the Promise-version of the function. >>> >>> In Node.js, we accomplish this by allowing a function to have a symbol >>> attached whose value is an alternative function that is returned by the >>> promisify function >>> >>> For instance, >>> >>> function myFunction(foo, bar, callback) { >>> callback(null, foo, bar); >>> } >>> myFunction[util.customPromisifySymbol] = async function(foo, bar) { >>> return [foo, bar]; >>> } >>> >>> const { promisify } = require('util'); >>> const mine = promisify(myFunction); >>> (async () => console.log(await mine('a','b')))(); >>> >>> As a convenience built into the language, it would be nice to be able to >>> short-circuit the need to call promisify with a special language-level >>> Symbol used specifically for this purpose: >>> >>> function myFunction(foo, bar, callback) { >>> callback(null, foo, bar); >>> } >>> myFunction[Symbol.await] = async function(foo, bar) { >>> return [foo, bar]; >>> } >>> >>> (async () => console.log(await myFunction('a','b')))(); >>> >>> The idea here is that if the function being awaited has the >>> [Symbol.await] property whose value is a function, then that function is >>> called when the await keyword is used. That is, >>> >>> myFunction('a', 'b', callback); // Invokes myFunction directly >>> await myFunction('a', 'b'); // Invokes myFunction[Symbol.await] >>> >>> if the Symbol.await property is not set or is not callable, then it >>> would fallback to default behavior. >>> >>> Automatic handling of this binding should also happen but has some >>> technical detail to work out: >>> >>> const obj = { >>> a: 1, >>> foo() {} >>> }; >>> obj.foo[Symbol.await] = async function() { >>> return this.a; >>> } >>> await obj.foo(); // Calls await obj.foo[Symbol.await] with bound this >>> >>> This approach would make it far easier for legacy code bases to make the >>> transition to async/await syntax while maintaining legacy compat. >>> >>> Before writing up a formal proposal, I wanted to solicit some feedback >>> on this approach to see what folks thought. >>> >>> - James >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss at mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss at mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> > > > -- > ---- > GitHub:https://github.com/wesleyolis > LinkedIn:https://www.linkedin.com/in/wesley-walter-anton-oliver-85466613b/ > Blog/Website:https://sites.google.com/site/wiprogamming/Home > Skype: wezley_oliver > MSN messenger: wesley.olis at gmail.com > -- ---- GitHub:https://github.com/wesleyolis LinkedIn:https://www.linkedin.com/in/wesley-walter-anton-oliver-85466613b/ Blog/Website:https://sites.google.com/site/wiprogamming/Home Skype: wezley_oliver MSN messenger: wesley.olis at gmail.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20200622/07e885e4/attachment.html>
For many legacy code bases that are based on callbacks mechanisms like node.js' promisify function are required to help facilitate the transition from callback centric code to Promise-centric. A lot of the time, these can follow straightforward rules without requiring customization. However, at other times it is necessary for user code to provide custom implementations of the Promise-version of the function.
In Node.js, we accomplish this by allowing a function to have a symbol attached whose value is an alternative function that is returned by the promisify function
For instance,
function myFunction(foo, bar, callback) { callback(null, foo, bar); } myFunction[util.customPromisifySymbol] = async function(foo, bar) { return [foo, bar]; }
const { promisify } = require('util'); const mine = promisify(myFunction); (async () => console.log(await mine('a','b')))();
As a convenience built into the language, it would be nice to be able to short-circuit the need to call promisify with a special language-level Symbol used specifically for this purpose:
function myFunction(foo, bar, callback) { callback(null, foo, bar); } myFunction[Symbol.await] = async function(foo, bar) { return [foo, bar]; }
(async () => console.log(await myFunction('a','b')))();
The idea here is that if the function being awaited has the [Symbol.await] property whose value is a function, then that function is called when the await keyword is used. That is,
myFunction('a', 'b', callback); // Invokes myFunction directly await myFunction('a', 'b'); // Invokes myFunction[Symbol.await]
if the Symbol.await property is not set or is not callable, then it would fallback to default behavior.
Automatic handling of this binding should also happen but has some technical detail to work out:
const obj = { a: 1, foo() {} }; obj.foo[Symbol.await] = async function() { return this.a; } await obj.foo(); // Calls await obj.foo[Symbol.await] with bound this
This approach would make it far easier for legacy code bases to make the transition to async/await syntax while maintaining legacy compat.
Before writing up a formal proposal, I wanted to solicit some feedback on this approach to see what folks thought.