Хотя вы можете собрать что-то вроде обещаний, в этом случае в этом нет необходимости.
Ваш код написан процедурным образом. Вы написали очень специфические c функции, которые извлекают определенные c биты из Post
и отбрасывают остальные. Вместо этого держите свои Post
вместе.
package main
import(
"fmt"
"encoding/json"
"net/http"
"sync"
)
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
UserID int `json:"userId"`
isCompleted bool `json:"completed"`
}
func fetchPost(id int) Post {
url := fmt.Sprintf("https://jsonplaceholder.typicode.com/todos/%v", id)
resp, err := http.Get(url)
if err != nil {
panic("HTTP error")
}
defer resp.Body.Close()
// It's more efficient to let json Decoder handle the IO.
var post Post
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&post)
if err != nil {
panic("Decoding error")
}
return post
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
var firstPost Post
var secondPost Post
go func() {
firstPost = fetchPost(1)
defer wg.Done()
}()
go func() {
secondPost = fetchPost(2)
defer wg.Done()
}()
wg.Wait()
fmt.Println("First post ID is", firstPost.ID)
fmt.Println("Second post ID is", secondPost.ID)
fmt.Println("Second post title is", secondPost.Title)
}
Теперь вместо кэширования ответов вы можете кэшировать сообщения. Мы можем сделать это, добавив PostManager для обработки извлечения и кэширования сообщений.
Обратите внимание, что обычный map
небезопасен для одновременного использования, поэтому мы используем syn c .Map для наш кеш.
type PostManager struct {
sync.Map
}
func (pc *PostManager) Fetch(id int) Post {
post, ok := pc.Load(id)
if ok {
return post.(Post)
}
post = pc.fetchPost(id)
pc.Store(id, post)
return post.(Post)
}
func (pc *PostManager) fetchPost(id int) Post {
url := fmt.Sprintf("https://jsonplaceholder.typicode.com/todos/%v", id)
resp, err := http.Get(url)
if err != nil {
panic("HTTP error")
}
defer resp.Body.Close()
// It's more efficient to let json Decoder handle the IO.
var post Post
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&post)
if err != nil {
panic("Decoding error")
}
return post
}
PostManager
методы должны получать указатель приемника, чтобы избежать копирования мьютекса внутри sync.Map
.
И вместо прямой загрузки сообщений мы используем PostManager.
func main() {
var postManager PostManager
var wg sync.WaitGroup
wg.Add(2)
var firstPost Post
var secondPost Post
go func() {
firstPost = postManager.Fetch(1)
defer wg.Done()
}()
go func() {
secondPost = postManager.Fetch(2)
defer wg.Done()
}()
wg.Wait()
fmt.Println("First post ID is", firstPost.ID)
fmt.Println("Second post ID is", secondPost.ID)
fmt.Println("Second post title is", secondPost.Title)
}
Кэширование PostManager было бы улучшено с помощью условных запросов , чтобы проверить, изменилось ли кэшированное сообщение.
Его блокировка также может быть улучшена, как написано можно получить один и тот же пост одновременно. Мы можем исправить это, используя singleflight
, чтобы разрешить только один вызов fetchPost
с указанным идентификатором за один раз.
type PostManager struct {
group singleflight.Group
cached sync.Map
}
func (pc *PostManager) Fetch(id int) Post {
post,ok := pc.cached.Load(id)
if !ok {
// Multiple calls with the same key at the same time will only run the code once, but all calls get the result.
post, _, _ = pc.group.Do(strconv.Itoa(id), func() (interface{}, error) {
post := pc.fetchPost(id)
pc.cached.Store(id, post)
return post, nil
})
}
return post.(Post)
}