diff --git a/x/io/README.md b/x/io/README.md index 79d76666..1ad9dad7 100644 --- a/x/io/README.md +++ b/x/io/README.md @@ -366,23 +366,77 @@ In some situations, you may want to get the first result of multiple async opera ## Design +Introduce `Promise` type to represent the eventual completion of an asynchronous operation and its resulting value. `Promise` can be resolved with a value or rejected with an error. `Promise` can be awaited to get the value or error. + +`Promise` just a type indicating the result of an asynchronous operation, it injected by the LLGo compiler, and the user can't create a `Promise` directly. + ```go -func resolveAfter1Second() Promise[string] { - return Async(func (context) { - context.ScheduleAfter(1 * time.Second, func() { - context.Resolve("Resolved after 1 second") - }) +// Some native async functions +func timeoutAsync(d time.Duration, cb func()) { + go func() { + time.Sleep(d) + cb() + }() +} + +// Wrap callback-based async function into Promise +func resolveAfter1Second() (resolve Promise[string]) { + timeoutAsync(1 * time.Second, func() { + resolve("Resolved after 1 second", nil) }) } -func asyncCall() Promise[string] { - return Async(resolveAfter1Second().Await()) +// Compiled to: +func resolveAfter1Second() (resolve PromiseImpl[string]) { + promise := io.NewPromiseImpl[string](resolve func(value string, err error) { + resolve: func(value string, err error) { + for true { + switch (promise.prev = promise.next) { + case 0: + timeoutAsync(1 * time.Second, func() { + resolve("Resolved after 1 second", nil) + }) + } + } + }, + } + return promise } +func asyncCall() (resolve Promise[string]) { + str, err := resolveAfter1Second().Await() + resolve("AsyncCall: " + str, err) +} + +// Compiled to: +func asyncCall() (resolve PromiseImpl[string]) { + promise := io.NewPromiseImpl[string](resolve func(value string, err error) { + for true { + switch (promise.prev = promise.next) { + case 0: + resolveAfter1Second() + return + case 1: + str, err := promise.value, promise.err + resolve("AsyncCall: " + str, err) + return + } + } + }) + return promise +} + +// Directly return Promise func asyncCall2() Promise[string] { return resolveAfter1Second() } +// Compiled to: +func asyncCall2() PromiseImpl[string] { + return resolveAfter1Second() +} + +// Don't wait for Promise to complete func asyncCall3() { resolveAfter1Second().Then(func(result string) { fmt.Println("AsyncCall3: " + result) diff --git a/x/io/_demo/asyncdemo/async.go b/x/io/_demo/asyncdemo/async.go index ce972fe7..b29751f8 100644 --- a/x/io/_demo/asyncdemo/async.go +++ b/x/io/_demo/asyncdemo/async.go @@ -18,84 +18,53 @@ func (r *Response) mock(body string) { r.mockBody = body } -func (r *Response) Text() *io.Promise[string] { - return io.NewPromise[string](func(resolve func(string, error)) { - resolve(r.mockBody, nil) - }) +func (r *Response) Text() (resolve io.Promise[string]) { + resolve(r.mockBody, nil) + return } func HttpGet(url string, callback func(resp *Response, err error)) { panic("todo: Get") } -func AsyncHttpGet(url string) io.AsyncCall[*Response] { - return io.NewPromise[*Response](func(resolve func(*Response, error)) { - HttpGet(url, resolve) - }) +func AsyncHttpGet(url string) (resolve io.Promise[*Response]) { + HttpGet(url, resolve) + return } type User struct { Name string } -func GetUser(uid string) io.AsyncCall[User] { - return io.NewPromise[User](func(resolve func(User, error)) { - resp, err := io.Await(AsyncHttpGet("http://example.com/user/" + uid)) - if err != nil { - resolve(User{}, err) - return - } +func GetUser(uid string) (resolve io.Promise[User]) { + resp, err := io.Await[*Response](AsyncHttpGet("http://example.com/user/" + uid)) + if err != nil { + resolve(User{}, err) + return + } - if resp.StatusCode != 200 { - resolve(User{}, fmt.Errorf("http status code: %d", resp.StatusCode)) - return - } + if resp.StatusCode != 200 { + resolve(User{}, fmt.Errorf("http status code: %d", resp.StatusCode)) + return + } - resp.mock(`{"name":"Alice"}`) + resp.mock(`{"name":"Alice"}`) - body, err := io.Await[string](resp.Text()) - if err != nil { - resolve(User{}, err) - return - } - user := User{} - if err := json.Unmarshal([]byte(body), &user); err != nil { - resolve(User{}, err) - return - } + body, err := io.Await[string](resp.Text()) + if err != nil { + resolve(User{}, err) + return + } + user := User{} + if err := json.Unmarshal([]byte(body), &user); err != nil { + resolve(User{}, err) + return + } - resolve(user, nil) - }) + resolve(user, nil) + return } -// func GetUser1(uid string) (resolve io.AsyncCall[User]) { -// resp, err := io.Await(AsyncHttpGet("http://example.com/user/" + uid)) -// if err != nil { -// resolve(User{}, err) -// return -// } - -// if resp.StatusCode != 200 { -// resolve(User{}, fmt.Errorf("http status code: %d", resp.StatusCode)) -// return -// } - -// resp.mock(`{"name":"Alice"}`) - -// body, err := io.Await[string](resp.Text()) -// if err != nil { -// resolve(User{}, err) -// return -// } -// user := User{} -// if err := json.Unmarshal([]byte(body), &user); err != nil { -// resolve(User{}, err) -// return -// } - -// resolve(user, nil) -// } - func GetScore() *io.Promise[float64] { panic("todo: GetScore") } @@ -108,9 +77,12 @@ func main() { user, err := GetUser("123").Await() fmt.Println(user, err) - user, err = io.Race(GetUser("123"), GetUser("456"), GetUser("789")).Await() + user, err = io.Race[User](GetUser("123"), GetUser("456"), GetUser("789")).Await() fmt.Println(user, err) + users, err := io.All[User]([]io.AsyncCall[User]{GetUser("123"), GetUser("456"), GetUser("789")}).Await() + fmt.Println(users, err) + user, score, _, err := io.Await3[User, float64, io.Void](GetUser("123"), GetScore(), DoUpdate("update sth.")) fmt.Println(user, score, err) diff --git a/x/io/io.go b/x/io/io.go index 7ceb650b..fde7428f 100644 --- a/x/io/io.go +++ b/x/io/io.go @@ -48,6 +48,10 @@ func Race[OutT any](acs ...AsyncCall[OutT]) (ret AsyncCall[OutT]) { return } +func All[OutT any](acs []AsyncCall[OutT]) (ret AsyncCall[[]OutT]) { + return nil +} + // llgo:link Await2 llgo.await func Await2[OutT1, OutT2 any]( ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2], @@ -64,31 +68,15 @@ func Await3[OutT1, OutT2, OutT3 any]( // ----------------------------------------------------------------------------- -type Promise[OutT any] struct { -} +type Promise[OutT any] func(OutT, error) -func NewPromise[OutT any](fn func(resolve func(OutT, error))) (ret *Promise[OutT]) { - ret = &Promise[OutT]{} +// llgo:link Promise.Await llgo.await +func (p Promise[OutT]) Await(timeout ...time.Duration) (ret OutT, err error) { return } -func NewPromiseFromValue[OutT any](value OutT) (ret *Promise[OutT]) { - return NewPromise[OutT](func(resolve func(OutT, error)) { - resolve(value, nil) - }) -} - -func (p *Promise[OutT]) Await(timeout ...time.Duration) (ret OutT, err error) { - return -} - -func (p *Promise[OutT]) Chan() <-chan OutT { +func (p Promise[OutT]) Chan() <-chan OutT { return nil } -// llgo:link Async llgo.async -func Async[OutT any](fn any) (ret Promise[OutT]) { - return -} - // -----------------------------------------------------------------------------