Promise的Golang实现,其行为符合Promises/A+规范,并参考ES/Promise规范实现,尽可能模拟了JavaScript事件循环中Promise的行为。
Promise-Go 提供了一个完整的异步编程解决方案,具有以下特点:
- 可以在不同
goroutine中创建、使用Promise,且能保证逻辑上的有序调用 Promise.Then中的回调函数长时间运行将会阻塞其他Promise实例的运行EventLoop.SetTimeout和EventLoop.SetInterval无法保证精确的调度,会受到goroutine繁忙的影响
Promise-Go 主要包含以下功能:
- 完整的
Promises/A+规范实现,支持链式调用 - 模拟
JavaScript事件循环的微任务队列和宏任务队列 - 批量处理:
All、AllSettled、Race、Any、Some - 超时组合子:
Timeout(为单个 Promise 设定超时,超时以TimeoutError拒绝) - 定时器:
SetTimeout、SetInterval、Delay - 迭代方法:
Map、Filter、Each、Reduce - 钩子函数:支持在
Promise生命周期的关键节点插入回调 - 错误类型:提供
TypeError、RangeError、TimeoutError、AggregateError等标准错误类型
完整的API文档:Promise-Go on Go Packages
事件循环是 Promise-Go 的核心组件,负责调度微任务和宏任务的执行。通过 StartEventLoop 启动一个事件循环,它会持续运行直到调用 Stop 方法关闭。
事件循环的执行顺序为:清空微队列 → 执行一个宏任务 → 清空微队列 → ...
el := promise.StartEventLoop(1) // 创建包含1个工作线程的事件循环
defer el.Stop() // 等待异步任务完成后关闭Promise 有三种状态:
- Pending(待定):初始状态,可能转换为
Fulfilled或Rejected - Fulfilled(已解决):操作成功完成,状态不可再变
- Rejected(已拒绝):操作失败,状态不可再变
Promise-Go 模拟了 JavaScript 的事件循环机制:
- 微任务队列:包括
Promise回调、QueueMicrotask添加的任务等,具有更高优先级 - 宏任务队列:包括
SetTimeout、SetInterval添加的定时任务等
go get github.com/TikaFlow/promise-gopackage main
import (
"fmt"
"time"
"github.com/TikaFlow/promise-go"
)
func main() {
el := promise.StartEventLoop(1)
defer el.Stop()
// 创建 Promise 并链式调用
p := el.NewPromise(func(resolve, reject func(v any)) error {
resolve("hello world")
return nil
})
p.Then(func(v any) (any, error) {
fmt.Println(v.(string))
return nil, nil
}, nil)
// 延时任务
el.SetTimeout(func() {
fmt.Println("[A]")
}, 30)
time.Sleep(time.Millisecond * 50)
}
Promise统一用any承载值,以符合Promises/A+允许任意已决类型及状态穿透的约定。
暂未实现与其他
Promise(thenable) 实现的互操作。
EventLoop 提供两套钩子系统:
在 Promise 生命周期的关键节点插入回调,回调签名为 func(p *Promise):
- PromiseCreated:
Promise实例被创建时 - PromiseChained:
Promise实例被链式调用(Then、Catch、Finally)时 - PromiseFulfilled:
Promise实例解决时 - PromiseRejected:
Promise实例拒绝时 - PromiseSettled:
Promise实例已决时(无论解决或拒绝)
// 注册钩子
key := el.OnPromise(promise.PromiseCreated, func(p *promise.Promise) {
fmt.Println("New promise created")
})
// 注销钩子
_ = el.OffPromise(PromiseCreated, key)当任务执行中发生 panic 时触发,回调签名为 func(r any)。所有 panic 先触发 AllPanic,再触发具体事件:
- AllPanic:所有 panic 的通用钩子,优先触发
- PromisePanic:Promise 回调发生 panic 时
- AsyncPanic:Async 任务发生 panic 时
- ExecutorPanic:Promise 执行器(executor)发生 panic 时
- TimerPanic:定时器任务发生 panic 时
- HookPanic:钩子函数自身发生 panic 时
// 注册 panic 钩子
key := el.OnPanic(promise.TimerPanic, func(r any) {
fmt.Printf("timer panic: %v\n", r)
})
// 注销 panic 钩子
_ = el.OffPanic(promise.TimerPanic, key)Promise实例一旦创建,执行器函数会立即同步调用resolve和reject函数一共只能调用一次,多次调用会被忽略- 如果给
resolve传递一个Promise实例,返回的Promise将跟随该实例的状态 - 如果解决值是
Promise本身,会抛出TypeError(防止循环引用) Then方法返回的新Promise状态由回调函数的执行结果决定- 回调或执行器函数抛出异常(
return err或panic)会导致新Promise被拒绝 - 微任务(
Promise回调)优先于宏任务(定时器)执行 - 不同
EventLoop之间不保证全局时序:当某个Promise采纳了另一事件循环的Promise时,其状态可能由另一事件循环的 goroutine 设置。EventLoop.Resolve对已是Promise的值会直接返回原对象、不重新绑定事件循环,因此链式调用可能跨事件循环。跨事件循环互操作仅保证状态一致性,不保证时序;如需确定时序,请让相关Promise属于同一个EventLoop
MIT License
Copyright (c) [2025] [兮夏]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.