Regenerate nvim config
This commit is contained in:
98
config/neovim/store/lazy-plugins/promise-async/lua/async.lua
Normal file
98
config/neovim/store/lazy-plugins/promise-async/lua/async.lua
Normal file
@ -0,0 +1,98 @@
|
||||
local promise = require('promise')
|
||||
local utils = require('promise-async.utils')
|
||||
local compat = require('promise-async.compat')
|
||||
|
||||
local asyncId = {'promise-async'}
|
||||
|
||||
---@class Async
|
||||
local Async = setmetatable({_id = asyncId}, {
|
||||
__call = function(self, executor)
|
||||
return self.sync(executor)
|
||||
end
|
||||
})
|
||||
|
||||
local packedId = {}
|
||||
|
||||
local Packed = {_id = packedId}
|
||||
Packed.__index = Packed
|
||||
|
||||
local function wrapPacked(packed)
|
||||
return setmetatable(packed, Packed)
|
||||
end
|
||||
|
||||
local function hasPacked(o)
|
||||
return type(o) == 'table' and o._id == packedId
|
||||
end
|
||||
|
||||
local function injectENV(fn)
|
||||
compat.setfenv(fn, setmetatable({
|
||||
await = Async.wait,
|
||||
pcall = compat.pcall,
|
||||
xpcall = compat.xpcall
|
||||
}, {
|
||||
__index = compat.getfenv(fn)
|
||||
}))
|
||||
end
|
||||
|
||||
function Async.sync(executor)
|
||||
local typ = type(executor)
|
||||
local isCallable, fn = utils.getCallable(executor, typ)
|
||||
assert(isCallable, 'a callable table or function expected, got ' .. typ)
|
||||
injectENV(fn)
|
||||
return promise:new(function(resolve, reject)
|
||||
local co = coroutine.create(typ == 'function' and executor or function()
|
||||
return executor()
|
||||
end)
|
||||
|
||||
local function afterResume(status, ...)
|
||||
if not status then
|
||||
local reason = select(1, ...)
|
||||
reject(debug.traceback(co, reason))
|
||||
return
|
||||
elseif coroutine.status(co) == 'dead' then
|
||||
local value
|
||||
local n = select('#', ...)
|
||||
if n == 1 then
|
||||
value = select(1, ...)
|
||||
elseif n > 1 then
|
||||
value = wrapPacked({...})
|
||||
end
|
||||
resolve(value)
|
||||
return
|
||||
end
|
||||
local p = select(1, ...)
|
||||
return p
|
||||
end
|
||||
|
||||
local function next(err, res)
|
||||
local p = afterResume(coroutine.resume(co, err, res))
|
||||
if p then
|
||||
p:thenCall(function(value)
|
||||
next(false, value)
|
||||
end, function(reason)
|
||||
next(true, reason)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
next()
|
||||
end)
|
||||
end
|
||||
|
||||
---Export wait function to someone needs, wait function actually have injected as `await` into
|
||||
---the executor of async function
|
||||
---@param p Promise|any
|
||||
---@return ...
|
||||
function Async.wait(p)
|
||||
p = promise.resolve(p)
|
||||
local err, res = coroutine.yield(p)
|
||||
if err then
|
||||
error(res, 0)
|
||||
elseif hasPacked(res) then
|
||||
return compat.unpack(res)
|
||||
else
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
return Async
|
||||
@ -0,0 +1,131 @@
|
||||
---Functions are compatible with LuaJIT's.
|
||||
---@class PromiseAsyncCompat
|
||||
local M = {}
|
||||
|
||||
---@return boolean
|
||||
function M.is51()
|
||||
return _G._VERSION:sub(-3) == '5.1' and not jit
|
||||
end
|
||||
|
||||
if table.pack then
|
||||
M.pack = table.pack
|
||||
else
|
||||
M.pack = function(...)
|
||||
return {n = select('#', ...), ...}
|
||||
end
|
||||
end
|
||||
|
||||
if table.unpack then
|
||||
M.unpack = table.unpack
|
||||
else
|
||||
M.unpack = unpack
|
||||
end
|
||||
---@diagnostic enable: deprecated
|
||||
|
||||
if M.is51() then
|
||||
local _pcall, _xpcall = pcall, xpcall
|
||||
local utils = require('promise-async.utils')
|
||||
|
||||
local function yieldInCoroutine(thread, co, success, ...)
|
||||
if coroutine.status(co) == 'suspended' then
|
||||
return yieldInCoroutine(thread, co, coroutine.resume(co, coroutine.yield(...)))
|
||||
end
|
||||
return success, ...
|
||||
end
|
||||
|
||||
local function doPcall(thread, f, ...)
|
||||
local typ = type(f)
|
||||
local ok, fn = utils.getCallable(f, typ)
|
||||
if not ok then
|
||||
return false, ('attempt to call a %s value'):format(typ)
|
||||
end
|
||||
local co = coroutine.create(function(...)
|
||||
return fn(...)
|
||||
end)
|
||||
return yieldInCoroutine(thread, co, coroutine.resume(co, ...))
|
||||
end
|
||||
|
||||
M.pcall = function(f, ...)
|
||||
local thread = coroutine.running()
|
||||
if not thread then
|
||||
return _pcall(f, ...)
|
||||
end
|
||||
return doPcall(thread, f, ...)
|
||||
end
|
||||
|
||||
local function xpcallCatch(msgh, success, ...)
|
||||
if success then
|
||||
return true, ...
|
||||
end
|
||||
local ok, result = _pcall(msgh, ...)
|
||||
return false, ok and result or 'error in error handling'
|
||||
end
|
||||
|
||||
M.xpcall = function(f, msgh, ...)
|
||||
local thread = coroutine.running()
|
||||
if not thread then
|
||||
return _xpcall(f, msgh, ...)
|
||||
end
|
||||
return xpcallCatch(msgh, doPcall(thread, f, ...))
|
||||
end
|
||||
else
|
||||
M.pcall = pcall
|
||||
M.xpcall = xpcall
|
||||
end
|
||||
|
||||
if setfenv then
|
||||
M.setfenv = setfenv
|
||||
M.getfenv = getfenv
|
||||
else
|
||||
local function findENV(f)
|
||||
local name = ''
|
||||
local value
|
||||
local up = 1
|
||||
while name do
|
||||
name, value = debug.getupvalue(f, up)
|
||||
if name == '_ENV' then
|
||||
return up, value
|
||||
end
|
||||
up = up + 1;
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function envHelper(f, name)
|
||||
if type(f) == 'number' then
|
||||
if f < 0 then
|
||||
error(([[bad argument #1 to '%s' (level must be non-negative)]]):format(name), 3)
|
||||
end
|
||||
local ok, dInfo = pcall(debug.getinfo, f + 2, 'f')
|
||||
if not ok or not dInfo then
|
||||
error(([[bad argument #1 to '%s' (invalid level)]]):format(name), 3)
|
||||
end
|
||||
f = dInfo.func
|
||||
elseif type(f) ~= 'function' then
|
||||
error(([[bad argument #1 to '%s' (number expected, got %s)]]):format(name, type(f)), 3)
|
||||
end
|
||||
return f
|
||||
end
|
||||
|
||||
function M.setfenv(f, table)
|
||||
f = envHelper(f, 'setfenv')
|
||||
local up = findENV(f)
|
||||
if up > 0 then
|
||||
debug.upvaluejoin(f, up, function()
|
||||
return table
|
||||
end, 1)
|
||||
end
|
||||
return f
|
||||
end
|
||||
|
||||
function M.getfenv(f)
|
||||
if f == 0 or f == nil then
|
||||
return _G
|
||||
end
|
||||
f = envHelper(f, 'getfenv')
|
||||
local up, value = findENV(f)
|
||||
return up > 0 and value or _G
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,115 @@
|
||||
local errorId = {}
|
||||
|
||||
---@class PromiseAsyncError
|
||||
---@field err any
|
||||
---@field queue string[]
|
||||
---@field index number
|
||||
local Error = {_id = errorId}
|
||||
Error.__index = Error
|
||||
|
||||
local function dump(o, limit)
|
||||
local s
|
||||
if type(o) ~= 'table' then
|
||||
s = tostring(o)
|
||||
else
|
||||
local meta = getmetatable(o)
|
||||
if meta and meta.__tostring then
|
||||
s = tostring(o)
|
||||
else
|
||||
if limit > 0 then
|
||||
local fmt = '%s [%s] = %s,'
|
||||
s = '{'
|
||||
for k, v in pairs(o) do
|
||||
if type(k) ~= 'number' then
|
||||
k = '"' .. k .. '"'
|
||||
end
|
||||
s = fmt:format(s, k, dump(v, limit - 1))
|
||||
end
|
||||
s = s:sub(1, #s - 1) .. ' }'
|
||||
else
|
||||
s = '{...}'
|
||||
end
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
function Error.isInstance(o)
|
||||
return type(o) == 'table' and o._id == errorId
|
||||
end
|
||||
|
||||
---@param thread? thread
|
||||
---@param level number
|
||||
---@param skipShortSrc? string
|
||||
---@return string?
|
||||
function Error.format(thread, level, skipShortSrc)
|
||||
local res
|
||||
local dInfo = thread and debug.getinfo(thread, level, 'nSl') or debug.getinfo(level, 'nSl')
|
||||
if dInfo then
|
||||
local name, shortSrc, currentline = dInfo.name, dInfo.short_src, dInfo.currentline
|
||||
if skipShortSrc == shortSrc then
|
||||
return
|
||||
end
|
||||
local detail
|
||||
if not name or name == '' then
|
||||
detail = ('in function <Anonymous:%d>'):format(dInfo.linedefined)
|
||||
else
|
||||
detail = ([[in function '%s']]):format(name)
|
||||
end
|
||||
res = (' %s:%d: %s'):format(shortSrc, currentline, detail)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
---@param err any
|
||||
---@return PromiseAsyncError
|
||||
function Error.new(err)
|
||||
local o = setmetatable({}, Error)
|
||||
o.err = err
|
||||
o.queue = {}
|
||||
o.index = 0
|
||||
return o
|
||||
end
|
||||
|
||||
function Error:__tostring()
|
||||
local errMsg = dump(self.err, 1)
|
||||
if #self.queue == 0 then
|
||||
return errMsg
|
||||
end
|
||||
local t = {}
|
||||
for i = 1, self.index do
|
||||
table.insert(t, self.queue[i])
|
||||
end
|
||||
table.insert(t, errMsg)
|
||||
if self.index < #self.queue then
|
||||
table.insert(t, 'stack traceback:')
|
||||
end
|
||||
for i = self.index + 1, #self.queue do
|
||||
table.insert(t, self.queue[i])
|
||||
end
|
||||
return table.concat(t, '\n')
|
||||
end
|
||||
|
||||
---@param value string
|
||||
function Error:unshift(value)
|
||||
if value then
|
||||
self.index = self.index + 1
|
||||
table.insert(self.queue, 1, value)
|
||||
end
|
||||
return #self.queue
|
||||
end
|
||||
|
||||
---@param value? string
|
||||
function Error:push(value)
|
||||
if value then
|
||||
table.insert(self.queue, value)
|
||||
end
|
||||
return #self.queue
|
||||
end
|
||||
|
||||
---@return any
|
||||
function Error:peek()
|
||||
return self.err
|
||||
end
|
||||
|
||||
return Error
|
||||
@ -0,0 +1,85 @@
|
||||
local uv = require('luv')
|
||||
|
||||
---@class PromiseAsyncLoop
|
||||
---@field tick userdata
|
||||
---@field tickCallbacks function[]
|
||||
---@field tickStarted boolean
|
||||
---@field idle userdata
|
||||
---@field idleCallbacks function[]
|
||||
---@field idleStarted boolean
|
||||
local EventLoop = {
|
||||
tick = uv.new_timer(),
|
||||
tickCallbacks = {},
|
||||
tickStarted = false,
|
||||
idle = uv.new_idle(),
|
||||
idleCallbacks = {},
|
||||
idleStarted = false
|
||||
}
|
||||
|
||||
function EventLoop.setTimeout(callback, ms)
|
||||
local timer = uv.new_timer()
|
||||
timer:start(ms, 0, function()
|
||||
timer:close()
|
||||
EventLoop.callWrapper(callback)
|
||||
end)
|
||||
return timer
|
||||
end
|
||||
|
||||
local function runTick()
|
||||
EventLoop.tickStarted = true
|
||||
local callbacks = EventLoop.tickCallbacks
|
||||
EventLoop.tickCallbacks = {}
|
||||
for _, cb in ipairs(callbacks) do
|
||||
EventLoop.callWrapper(cb)
|
||||
end
|
||||
if #EventLoop.tickCallbacks > 0 then
|
||||
EventLoop.tick:start(0, 0, runTick)
|
||||
else
|
||||
EventLoop.tickStarted = false
|
||||
end
|
||||
end
|
||||
|
||||
function EventLoop.nextTick(callback)
|
||||
table.insert(EventLoop.tickCallbacks, callback)
|
||||
if not EventLoop.tickStarted then
|
||||
EventLoop.tick:start(0, 0, runTick)
|
||||
end
|
||||
end
|
||||
|
||||
local function runIdle()
|
||||
EventLoop.idleStarted = true
|
||||
local callbacks = EventLoop.idleCallbacks
|
||||
EventLoop.idleCallbacks = {}
|
||||
for _, cb in ipairs(callbacks) do
|
||||
EventLoop.callWrapper(cb)
|
||||
end
|
||||
if #EventLoop.idleCallbacks > 0 then
|
||||
EventLoop.idle:start(runIdle)
|
||||
else
|
||||
EventLoop.idle:stop()
|
||||
EventLoop.idleStarted = false
|
||||
end
|
||||
end
|
||||
|
||||
function EventLoop.nextIdle(callback)
|
||||
EventLoop.nextTick(function()
|
||||
table.insert(EventLoop.idleCallbacks, callback)
|
||||
if not EventLoop.idleStarted then
|
||||
EventLoop.idle:start(runIdle)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if vim and type(vim.schedule) == 'function' then
|
||||
EventLoop.callWrapper = vim.schedule
|
||||
else
|
||||
function EventLoop.callWrapper(fn)
|
||||
local ok, res = pcall(fn)
|
||||
if not ok then
|
||||
-- luv can't handle object with __tostring filed
|
||||
error(tostring(res))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return EventLoop
|
||||
@ -0,0 +1,33 @@
|
||||
---@class PromiseAsyncUtils
|
||||
local M = {}
|
||||
|
||||
---@param o any
|
||||
---@param expectedType string
|
||||
function M.assertType(o, expectedType)
|
||||
local gotType = type(o)
|
||||
local fmt = '%s expected, got %s'
|
||||
return assert(gotType == expectedType, fmt:format(expectedType, gotType))
|
||||
end
|
||||
|
||||
---@param o any
|
||||
---@param typ? string
|
||||
---@return boolean, function|table|any
|
||||
function M.getCallable(o, typ)
|
||||
local ok
|
||||
local f
|
||||
local t = typ or type(o)
|
||||
if t == 'function' then
|
||||
ok, f = true, o
|
||||
elseif t ~= 'table' then
|
||||
ok, f = false, o
|
||||
else
|
||||
local meta = getmetatable(o)
|
||||
ok = meta and type(meta.__call) == 'function'
|
||||
if ok then
|
||||
f = meta.__call
|
||||
end
|
||||
end
|
||||
return ok, f
|
||||
end
|
||||
|
||||
return M
|
||||
410
config/neovim/store/lazy-plugins/promise-async/lua/promise.lua
Normal file
410
config/neovim/store/lazy-plugins/promise-async/lua/promise.lua
Normal file
@ -0,0 +1,410 @@
|
||||
local utils = require('promise-async.utils')
|
||||
local promiseId = {'promise-async'}
|
||||
local errFactory = require('promise-async.error')
|
||||
local shortSrc = debug.getinfo(1, 'S').short_src
|
||||
|
||||
---@alias PromiseState
|
||||
---| 1 #PENDING
|
||||
---| 2 #FULFILLED
|
||||
---| 3 #REJECTED
|
||||
local PENDING = 1
|
||||
local FULFILLED = 2
|
||||
local REJECTED = 3
|
||||
|
||||
--
|
||||
---@class Promise
|
||||
---@field state PromiseState
|
||||
---@field result any
|
||||
---@field queue table
|
||||
---@field needHandleRejection? boolean
|
||||
---@field err? PromiseAsyncError
|
||||
local Promise = setmetatable({_id = promiseId}, {
|
||||
__call = function(self, executor)
|
||||
return self:new(executor)
|
||||
end
|
||||
})
|
||||
Promise.__index = Promise
|
||||
|
||||
local function loadEventLoop()
|
||||
local success, res = pcall(require, 'promise-async.loop')
|
||||
assert(success, 'Promise need an EventLoop, ' ..
|
||||
'luv module or a customized EventLoop module is expected.')
|
||||
return res
|
||||
end
|
||||
|
||||
if vim then
|
||||
-- `require` in Neovim is hacked by its get_runtime API, may throw an error while calling
|
||||
-- `require` in libuv, require at once as a workaround.
|
||||
Promise.loop = require('promise-async.loop')
|
||||
else
|
||||
Promise.loop = setmetatable({}, {
|
||||
__index = function(_, key)
|
||||
local loop = loadEventLoop()
|
||||
rawset(Promise, 'loop', loop)
|
||||
return loop[key]
|
||||
end,
|
||||
__newindex = function(_, key, value)
|
||||
local loop = loadEventLoop()
|
||||
rawset(Promise, 'loop', loop)
|
||||
Promise.loop[key] = value
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
function Promise:__tostring()
|
||||
local state = self.state
|
||||
if state == PENDING then
|
||||
return 'Promise { <pending> }'
|
||||
elseif state == REJECTED then
|
||||
return ('Promise { <rejected> %s }'):format(tostring(self.result))
|
||||
else
|
||||
return ('Promise { <fulfilled> %s }'):format(tostring(self.result))
|
||||
end
|
||||
end
|
||||
|
||||
local function noop() end
|
||||
|
||||
---@param o any
|
||||
---@param typ? string
|
||||
---@return boolean
|
||||
function Promise.isInstance(o, typ)
|
||||
return (typ or type(o)) == 'table' and o._id == promiseId
|
||||
end
|
||||
|
||||
---must get `thenCall` field from `o` at one time, can't call repeatedly.
|
||||
---@param o any
|
||||
---@param typ? type
|
||||
---@return function?
|
||||
local function getThenable(o, typ)
|
||||
local thenCall
|
||||
if (typ or type(o)) == 'table' then
|
||||
thenCall = o.thenCall
|
||||
if type(thenCall) ~= 'function' then
|
||||
thenCall = nil
|
||||
end
|
||||
end
|
||||
return thenCall
|
||||
end
|
||||
|
||||
---@param err any
|
||||
---@return PromiseAsyncError
|
||||
local function buildError(err)
|
||||
local o = errFactory.new(err)
|
||||
local level = 4
|
||||
local value
|
||||
local thread = coroutine.running()
|
||||
repeat
|
||||
value = errFactory.format(thread, level, shortSrc)
|
||||
level = level + 1
|
||||
o:push(value)
|
||||
until not value
|
||||
table.remove(o.queue)
|
||||
return o
|
||||
end
|
||||
|
||||
local resolvePromise, rejectPromise
|
||||
|
||||
---@param promise Promise
|
||||
local function handleQueue(promise)
|
||||
local queue = promise.queue
|
||||
if #queue == 0 then
|
||||
return
|
||||
end
|
||||
if promise.needHandleRejection and #queue > 0 then
|
||||
promise.needHandleRejection = nil
|
||||
end
|
||||
promise.queue = {}
|
||||
|
||||
Promise.loop.nextTick(function()
|
||||
local state, result = promise.state, promise.result
|
||||
for _, q in ipairs(queue) do
|
||||
local newPromise, onFulfilled, onRejected = q[1], q[2], q[3]
|
||||
local func
|
||||
if state == FULFILLED then
|
||||
if utils.getCallable(onFulfilled) then
|
||||
func = onFulfilled
|
||||
else
|
||||
resolvePromise(newPromise, result)
|
||||
end
|
||||
elseif state == REJECTED then
|
||||
if utils.getCallable(onRejected) then
|
||||
func = onRejected
|
||||
else
|
||||
rejectPromise(newPromise, result)
|
||||
end
|
||||
end
|
||||
if func then
|
||||
local ok, res = xpcall(function()
|
||||
return func(result)
|
||||
end, function(errmsg)
|
||||
if type(errmsg) == 'string' then
|
||||
newPromise.err = buildError(errmsg)
|
||||
end
|
||||
return errmsg
|
||||
end)
|
||||
if ok then
|
||||
resolvePromise(newPromise, res)
|
||||
else
|
||||
rejectPromise(newPromise, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---@param promise Promise
|
||||
---@param result any
|
||||
---@param state PromiseState
|
||||
local function transition(promise, result, state)
|
||||
if promise.state ~= PENDING then
|
||||
return
|
||||
end
|
||||
promise.result = result
|
||||
promise.state = state
|
||||
handleQueue(promise)
|
||||
end
|
||||
|
||||
---@param promise Promise
|
||||
---@param executor PromiseExecutor
|
||||
---@param self? table
|
||||
local function wrapExecutor(promise, executor, self)
|
||||
local called = false
|
||||
local resolve = function(value)
|
||||
if called then
|
||||
return
|
||||
end
|
||||
resolvePromise(promise, value)
|
||||
called = true
|
||||
end
|
||||
local reject = function(reason)
|
||||
if called then
|
||||
return
|
||||
end
|
||||
rejectPromise(promise, reason)
|
||||
called = true
|
||||
end
|
||||
|
||||
local ok, res = xpcall(function()
|
||||
if self then
|
||||
---@diagnostic disable-next-line: redundant-parameter, param-type-mismatch
|
||||
return executor(self, resolve, reject)
|
||||
else
|
||||
return executor(resolve, reject)
|
||||
end
|
||||
end, function(errmsg)
|
||||
if type(errmsg) == 'string' then
|
||||
promise.err = buildError(errmsg)
|
||||
end
|
||||
return errmsg
|
||||
end)
|
||||
if not ok and not called then
|
||||
reject(res)
|
||||
end
|
||||
end
|
||||
|
||||
---@param promise Promise
|
||||
local function handleRejection(promise)
|
||||
promise.needHandleRejection = true
|
||||
|
||||
Promise.loop.nextIdle(function()
|
||||
if promise.needHandleRejection then
|
||||
promise.needHandleRejection = nil
|
||||
local err = promise.err
|
||||
if not err then
|
||||
err = errFactory.new(promise.result)
|
||||
end
|
||||
err:unshift('UnhandledPromiseRejection with the reason:')
|
||||
error(err)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---@param promise Promise
|
||||
---@param reason any
|
||||
rejectPromise = function(promise, reason)
|
||||
handleRejection(promise)
|
||||
transition(promise, reason, REJECTED)
|
||||
end
|
||||
|
||||
---@param promise Promise
|
||||
---@param value any
|
||||
resolvePromise = function(promise, value)
|
||||
if promise == value then
|
||||
local reason = debug.traceback('TypeError: Chaining cycle detected for promise')
|
||||
rejectPromise(promise, reason)
|
||||
return
|
||||
end
|
||||
|
||||
local valueType = type(value)
|
||||
if Promise.isInstance(value, valueType) then
|
||||
value:thenCall(function(val)
|
||||
resolvePromise(promise, val)
|
||||
end, function(reason)
|
||||
rejectPromise(promise, reason)
|
||||
end)
|
||||
else
|
||||
local thenCall = getThenable(value, valueType)
|
||||
if thenCall then
|
||||
wrapExecutor(promise, thenCall, value)
|
||||
else
|
||||
transition(promise, value, FULFILLED)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Promise:new(executor)
|
||||
utils.assertType(executor, 'function')
|
||||
local o = self == Promise and setmetatable({}, self) or self
|
||||
o.state = PENDING
|
||||
o.result = nil
|
||||
o.queue = {}
|
||||
o.needHandleRejection = nil
|
||||
|
||||
if executor ~= noop then
|
||||
wrapExecutor(o, executor)
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
function Promise:thenCall(onFulfilled, onRejected)
|
||||
local o = self.new(Promise, noop)
|
||||
table.insert(self.queue, {o, onFulfilled, onRejected})
|
||||
if self.state ~= PENDING then
|
||||
handleQueue(self)
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
function Promise:catch(onRejected)
|
||||
return self:thenCall(nil, onRejected)
|
||||
end
|
||||
|
||||
function Promise:finally(onFinally)
|
||||
local function wrapFinally()
|
||||
if utils.getCallable(onFinally) then
|
||||
---@diagnostic disable-next-line: need-check-nil
|
||||
onFinally()
|
||||
end
|
||||
end
|
||||
|
||||
return self:thenCall(function(value)
|
||||
wrapFinally()
|
||||
return value
|
||||
end, function(reason)
|
||||
wrapFinally()
|
||||
return Promise.reject(reason)
|
||||
end)
|
||||
end
|
||||
|
||||
function Promise.resolve(value)
|
||||
local typ = type(value)
|
||||
if Promise.isInstance(value, typ) then
|
||||
return value
|
||||
else
|
||||
local o = Promise:new(noop)
|
||||
local thenCall = getThenable(value, typ)
|
||||
if thenCall then
|
||||
wrapExecutor(o, thenCall, value)
|
||||
else
|
||||
o.state = FULFILLED
|
||||
o.result = value
|
||||
end
|
||||
return o
|
||||
end
|
||||
end
|
||||
|
||||
function Promise.reject(reason)
|
||||
local o = Promise:new(noop)
|
||||
o.state = REJECTED
|
||||
o.result = reason
|
||||
handleRejection(o)
|
||||
return o
|
||||
end
|
||||
|
||||
function Promise.all(values)
|
||||
utils.assertType(values, 'table')
|
||||
return Promise:new(function(resolve, reject)
|
||||
local res = {}
|
||||
local cnt = 0
|
||||
for k, v in pairs(values) do
|
||||
cnt = cnt + 1
|
||||
Promise.resolve(v):thenCall(function(value)
|
||||
res[k] = value
|
||||
cnt = cnt - 1
|
||||
if cnt == 0 then
|
||||
resolve(res)
|
||||
end
|
||||
end, function(reason)
|
||||
reject(reason)
|
||||
end)
|
||||
end
|
||||
if cnt == 0 then
|
||||
resolve(res)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function Promise.allSettled(values)
|
||||
utils.assertType(values, 'table')
|
||||
return Promise:new(function(resolve, reject)
|
||||
local res = {}
|
||||
local cnt = 0
|
||||
local _ = reject
|
||||
for k, v in pairs(values) do
|
||||
cnt = cnt + 1
|
||||
Promise.resolve(v):thenCall(function(value)
|
||||
res[k] = {status = 'fulfilled', value = value}
|
||||
end, function(reason)
|
||||
res[k] = {status = 'rejected', reason = reason}
|
||||
end):finally(function()
|
||||
cnt = cnt - 1
|
||||
if cnt == 0 then
|
||||
resolve(res)
|
||||
end
|
||||
end)
|
||||
end
|
||||
if cnt == 0 then
|
||||
resolve(res)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function Promise.any(values)
|
||||
utils.assertType(values, 'table')
|
||||
return Promise:new(function(resolve, reject)
|
||||
local cnt = 0
|
||||
local function rejectAggregateError()
|
||||
if cnt == 0 then
|
||||
reject('AggregateError: All promises were rejected')
|
||||
end
|
||||
end
|
||||
|
||||
for _, p in pairs(values) do
|
||||
cnt = cnt + 1
|
||||
Promise.resolve(p):thenCall(function(value)
|
||||
resolve(value)
|
||||
end, function()
|
||||
end):finally(function()
|
||||
cnt = cnt - 1
|
||||
rejectAggregateError()
|
||||
end)
|
||||
end
|
||||
rejectAggregateError()
|
||||
end)
|
||||
end
|
||||
|
||||
function Promise.race(values)
|
||||
utils.assertType(values, 'table')
|
||||
return Promise:new(function(resolve, reject)
|
||||
for _, p in pairs(values) do
|
||||
Promise.resolve(p):thenCall(function(value)
|
||||
resolve(value)
|
||||
end, function(reason)
|
||||
reject(reason)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return Promise
|
||||
Reference in New Issue
Block a user