DAY 6:Feature Pattern,我把未来托付给你了!

什麽是 Future Pattern?

呼叫者将 task 交给 goroutine 执行,执行完毕後 goroutine 将 task 运行得到的结果传回呼叫者

在昨天讲解完 Thread-Per-Message Pattern 後,将 message 以 goroutine 执行後,是没办法获得回传值的,如果有此需求,可以搭配 Future Pattern

当 goroutine 运行後,呼叫者需要有一个中间物件来去取得 goroutine 未来运行的结果,golang 通常采用 channel 实作

问题情境

延续推播新闻系统的情境,将新的新闻直接推播出去,除了推播系统效率要高,还需纪录推播完成的时间

相关的 code 在Github - go-design-patterns

昨天的 code 并没办法接收到PushNews()的回传运行完结束的时间值:

package main

import (
	"fmt"
	"time"
)

func PushNews(news string, startTime time.Time) time.Time {
	time.Sleep(time.Duration(3 * time.Second)) //模拟推播运行的时间
	fmt.Printf("%s cost %s\n", news, time.Since(startTime))
	return time.Now()
}

func main() {
	start := time.Now()
	allNews := []string{
		"中秋节来了",
		"记得",
		"不要户外烤肉~",
	}
	for _, news := range allNews {
		go PushNews(news, start)
	}
	time.Sleep(10 * time.Second) //等待goroutine执行完毕
}

解决方式

将 golang channel,实作至PushNews()中。goroutine 运行後,将newsCh{}先回传给呼叫者main(),在**未来(feature)**时 goroutine 会将发送完毕的时间time.Now()传至 channel,呼叫者main()只需在需要时等待 channel 收到time.Now()出现,如下:

package main

import (
	"fmt"
	"time"
)

func PushNews(news string, startTime time.Time) <-chan time.Time {
	newsCh := make(chan time.Time)
	go func() {
		time.Sleep(time.Duration(3 * time.Second)) //模拟推播运行的时间
		fmt.Printf("%s cost %s\n", news, time.Since(startTime))
		newsCh <- time.Now()
	}()
	return newsCh
}

func main() {
	start := time.Now()
	allNews := []string{
		"中秋节来了",
		"记得",
		"不要户外烤肉~",
	}
	newsChs := []<-chan time.Time{}
	for _, news := range allNews {
		newsChs = append(newsChs, PushNews(news, start))
	}

	// do something

	for index, newsCh := range newsChs {
		fmt.Printf("news %d is sent at %s\n", index, <-newsCh)
	}
}

先透过for _, news := range allNews将所有的新闻发送以启动 goroutine,但这边仅是启动,并取得一个中间物件 channel,等到有需要再从 channel 取得资料,甚至可以先在// do somethins处做其他事情。

最後在for index, newsCh := range newsChs取得 channel 的资料,如Guarded Suspension Pattern yorktodo所说,channel 会等到有资料再运行,否则等待,所以此处会等到所有<-newsCh都接收完毕才会离开 for 回圈运行完main(),如下:

与常见的 javascript promise 对比

promise 与 feature 是相似的概念,差异是:

  • promise 是以.then(function)的风格传送 function 给 promise 来去实作取得资料後的动作
  • feature 是以.get()或者 golang <-channel的方式取得资料後再呼叫处实作後续动作

但两者精神相同,都是处理如何异步取得资料。

如果熟悉 javascript 的话,可以用Promise.all()去思考此范例,如下:

const pushNews = (news, startTime) =>
  new Promise((resolve) =>
    setTimeout(() => {
      console.log(`${news} cost ${Date.now() - startTime}`);
      resolve(Date.now());
    }, 3000)
  );

const start = Date.now();
Promise.all(
  ["中秋节来了", "记得", "不要户外烤肉~"].map((news) => pushNews(news, start))
).then((allNews) =>
  allNews.map((finishTimes, index) =>
    console.log(`"news ${index} is sent at ${finishTimes}"`)
  )
);

// do something

会发现for _, news := range allNewsPromise.all()相同都是在启动异步的 code,等到异步的资料都会来就以.then(function)中的 function 来处理,由於示异步,我们也可以在程序// do something处执行其他事情而不被 block


<<:  IIFEs 立即函式:不需呼叫即可执行

>>:  Day 4: 人工智慧在音乐领域的应用 (AI发展史与简介 - 第一次寒冬)

Day3 - 登入登出相关问题

在安装完Shioaji套件之後,我们就可以开始使用api的功能了。 第一步当然是要登入我们的帐户啦,...

【第一天 - Leetcode 介绍】

Q1. 什麽是 Leetcode ? Leetcode 是一个线上练程序网站,收集了许多软件工程师面...

运行系统档案检查工具以修复Windows 10中损坏的档案

您所使用的Windows 10是否经常崩溃?您是否在Windows 10中收到档案丢失或损坏的错误?...

Day-24 快速面试之考题大公开!(3)

听听其他人对於快速面试的应对。 有被问到专案的优化方式如何,当时我答不好(冏)後来听到组员回答的很...

DAY07 资料视觉化

一、视觉化为何如此重要 终於进入到视觉化的部分了!虽然现在有很多的绘图软件,但我认为初期用pytho...