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

Promise eval in repl yields inaccessible object #18905

Closed
tomas opened this issue Feb 21, 2018 · 10 comments
Closed

Promise eval in repl yields inaccessible object #18905

tomas opened this issue Feb 21, 2018 · 10 comments
Labels
repl Issues and PRs related to the REPL subsystem.

Comments

@tomas
Copy link

tomas commented Feb 21, 2018

  • Version: v8.9.4
  • Platform: Linux x64
  • Subsystem: repl

I'm trying to build a custom repl that yields promises before returning the value to the user, but for some reason I'm unable to "reach" the final value, even though it does appear to be correctly assigned.

This is the code I'm using:

var repl = require('repl'),
    vm   = require('vm');

function foo() {
  return new Promise(function(resolve, reject) {
    resolve({ success: true })
  })
}

function myEval(code, context, file, cb) {
  var res = vm.runInContext(code, context)

  if (!res || typeof res.then != 'function') // Non-thenable response
    return cb(null, res)

  res.then(function(val) {
    cb(null, val)
  }, function(err) {
    cb(err)
  })
}

var replServer = repl.start({
  eval: myEval
})

replServer.context.foo = foo;

And this is what happens:

> res = foo()
{ success: true }
> res.success
undefined
> res.constructor
[Function: Promise]

However, the "real" result does get correctly assigned to _:

> res = foo()
{ success: true }
> _.success
true

Is there something I'm missing?

Thanks!

@Fishrock123 Fishrock123 added repl Issues and PRs related to the REPL subsystem. question Issues that look for answers. and removed question Issues that look for answers. labels Feb 21, 2018
@princejwesley
Copy link
Contributor

princejwesley commented Feb 22, 2018

foo() returns promise in res = foo() expression and therefore, res is a promise instance.
{ success: true } its just a REPL output expression which is specifically handled(with custom eval function) for promise instance.

> res = foo()
{ success: true }
> res   <- this should return { success: true } (from custom eval)
{ success: true }

What you need is something like res = await foo(); which will be available in the future release.

Closing this ticket, feel free to reopen it.

@tomas
Copy link
Author

tomas commented Feb 23, 2018

@princejwesley I see, thanks for the heads up. It does seem a bit confusing though. I mean, if res is actually a Promise instance then why is the repl output showing { success: true }? Why doesn't it show the actual value that it holds?

Well, I ended up figuring it out anyway. I'm now able to get the "real" promise.then result assigned to the variable, so the repl feels synchronous and provides a much nicer experience.

@princejwesley
Copy link
Contributor

@tomas Your custom eval function (myEval) handles Promise.then and emits { success: true } as output to terminal.

@tomas
Copy link
Author

tomas commented Feb 23, 2018

Is that a question? You can look at my original custom eval function in the OP.

The one I'm using now is way more complex.

@princejwesley
Copy link
Contributor

@tomas No, its not a question. Just trying to clear the doubt part. Your custom eval function runs the expression in a given context (res is still promise instance) and decides how to emit the resulting value(res) in the terminal.

@tomas
Copy link
Author

tomas commented Feb 23, 2018

OK. I still don't see the point why it works like this. Shouldn't what you see be what you get?

@devsnek
Copy link
Member

devsnek commented Feb 23, 2018

> res = foo() // res is set to calling foo which returns an unresolved promise
// `res = foo()` returns the promise as well, which is then handled with:
// res.then(function(val) {
//   cb(null, val)
//   ...

// which then calls back with the resolve value of the promise as the return value
{ success: true }
// however res is still set to the promise result of foo(), nothing has changed that yet

what you want is to parse the expression and perform an assignment to the variable like

res.then(function(val) {
  eval(`${variable} = val`);
  cb(null, val)
  ...

in #15566 we use acorn (a javascript syntax parser/lexer/tokenizer) to split the assignment, you can do something similar

@tomas
Copy link
Author

tomas commented Feb 23, 2018

@devsnek Thanks for a clearer explanation. The eval trick is actually what I'm doing now.

// however res is still set to the promise result of foo(), nothing has changed that yet

That's precisely my point. If res is still the result of foo() (the unresolved promise), then why does the repl show the result of the promised function when inspecting it? It should output something like Promise { ... }, don't you think?

@devsnek
Copy link
Member

devsnek commented Feb 23, 2018

no, you call the callback with the resolved value ({ success: true }) not the return value of the evaluated expression (Promise { <pending> })

@tomas
Copy link
Author

tomas commented Feb 23, 2018

Ok, thanks, and sorry for the confusion. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
repl Issues and PRs related to the REPL subsystem.
Projects
None yet
Development

No branches or pull requests

4 participants