rogchap / v8go

Execute JavaScript from Go
https://rogchap.com/v8go
BSD 3-Clause "New" or "Revised" License
3.21k stars 221 forks source link

How to get value from async function all? #338

Open ppedziwiatr opened 2 years ago

ppedziwiatr commented 2 years ago

Hey,

assuming this code:

func main() {
    iso := v8.NewIsolate()
    defer iso.Dispose()
    ctx := v8.NewContext(iso)
    defer ctx.Close()

    // TODO: load contract definition from Warp GW
    ctx.RunScript(`
    const handle = async function handle(state, action) {
      if (action.input.function === "add") {
        state.counter++;
        return { state };
      }
    }`, "contract.js")

    // TODO: load contract interactions from Warp GW
    ctx.RunScript(`
    let result = await handle({counter: 0}, {caller: 'ppe', input: {function: 'add'}})
    result = await handle(result.state, {caller: 'ppe', input: {function: 'add'}})
    result = await handle(result.state, {caller: 'ppe', input: {function: 'add'}})
`, "interactions.js")

    val, _ := ctx.RunScript("JSON.stringify(result)", "value.js")

    fmt.Printf("contract state: %s", val)
}

how to properly handle a promise and get a value from result?

Ofc. above example without async/await works flawlessly.

ti2ger92 commented 2 years ago

+1, I'm also stuck on this.

snej commented 1 year ago

In Go you have to check if the result value from V8 is a promise; if it is, you check its state. As long as it's still in the unresolved state, you call the context's PerformMicrotaskCheckpoint method to run a bit longer, then recheck the promise state.

func resolvePromise(val *v8.Value, err error) (*v8.Value, error) {
    if err != nil || !val.IsPromise() {
        return val, err
    }
    for {
        switch p, _ := val.AsPromise(); p.State() {
        case v8.Fulfilled:
            return p.Result(), nil
        case v8.Rejected:
            return nil, errors.New(p.Result().DetailString())
        case v8.Pending:
            r.ctx.PerformMicrotaskCheckpoint() // run VM to make progress on the promise
            // go round the loop again...
        default:
            return nil, fmt.Errorf("illegal v8.Promise state %d", p) // unreachable
        }
    }
}