Git Product home page Git Product logo

promise-async's Introduction

promise-async

GitHub Test GitHub Lint

The goal of promise-async is to port Promise & Async from JavaScript to Lua.

A value returned by async function in JavaScript is actually a Promise Object. It's incomplete and inflexible for using an async function wrapped by bare coroutine without Promise in almost Lua implementation.

Features

  • API is similar to JavaScript's
  • Customize EventLoop in any platforms
  • Support Lua 5.1-5.4 and LuaJIT with an EventLoop module
  • Support Neovim platform

Demonstration

promise-async-demo.mp4

Script

demo.lua

local function defuse(ms)
return promise:new(function(resolve, reject)
setTimeout(function()
resolve(ms)
end, ms)
end)
end
local function bomb(ms)
-- getmetatable(promise).__call = promise.new
return promise(function(resolve, reject)
setTimeout(function()
reject(ms)
end, ms)
end)
end
local function race()
return async(function()
return promise.race({
defuse(math.random(500, 1000)),
bomb(math.random(800, 1000))
})
end)
end
local notify = vim and vim.notify or print
local function play()
return async(function()
-- We are not in the next tick until first `await` is called.
notify('Game start!')
local cnt = 0
xpcall(function()
while true do
local ms = await(race())
cnt = cnt + ms
notify(('Defuse after %dms~'):format(ms))
end
end, function(msErr)
cnt = cnt + msErr
notify(('Bomb after %dms~'):format(msErr))
end)
notify(('Game end after %dms!'):format(cnt))
await {
thenCall = function(self, resolve, reject)
setTimeout(function()
reject(self.message)
end, 1000)
end,
message = 'try to throw an error :)'
}
end)
end
promise.resolve():thenCall(function(value)
notify('In next tick')
end)
notify('In main')
play():finally(function()
print('Before throwing UnhandledPromiseRejection on finally!')
end)

demo.js

async function defuse(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(ms)
}, ms)
})
}
async function bomb(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(ms)
}, ms)
})
}
async function race() {
return Promise.race([
defuse(500 + Math.ceil(Math.random() * 500)),
bomb(800 + Math.ceil(Math.random() * 200)),
])
}
async function play() {
console.info('Game start!')
let cnt = 0
try {
while (true) {
let ms = await race()
cnt = cnt + ms
console.info(`Defuse after ${ms}ms~`)
}
} catch (msErr) {
cnt = cnt + msErr
console.info(`Bomb after ${msErr}ms~`)
}
console.info(`Game end after ${cnt}ms!`)
await {
then: function(resolve, reject) {
setTimeout(() => {
reject(this.message)
}, 1000)
},
message: 'try to throw an error :)'
}
}
Promise.resolve().then((value) => {
console.info('In next tick')
})
console.info('In main')
play().finally(() => {
console.info('Before throwing UnhandledPromiseRejection on finally!')
})

Quickstart

Requirements

  • Lua 5.1 or latter
  • Luv

Luv is a default EventLoop for promise-async. It doesn't mean promise-async must require it. In fact, promise-async require a general EventLoop module which Luv like.

Installation

As a plugin for Neovim platform

Install with Packer.nvim:

  • As a normal plugin
use {'kevinhwang91/promise-async'}

or

  • As a Luarocks plugin
use_rocks {'promise-async'}

As a library from Luarocks

  1. luarocks install promise-async
  2. luarocks install luv or implement an EventLoop interface to adapt your platform

Documentation

promise-async's API is based on MDN-Promise. typings/promise.lua is the typings with documentation of Promise class.

Summary

Summary up the API different from JavaScript.

JavaScript Lua
new Promise Promise:new/Promise
Promise.then Promise:thenCall, then is language keyword
Promise.catch Promise:catch
Promise.finally Promise:finally
Promise.resolve Promise.resolve
Promise.reject Promise.reject
Promise.all: Symbol.iterator as iterator Promise.all: pairs as iterator
Promise.allSettled: Symbol.iterator as iterator Promise.allSettled: pairs as iterator
Promise.any: Symbol.iterator as iterator Promise.any: pairs as iterator
Promise.race: Symbol.iterator as iterator Promise.race: pairs as iterator
async: as keyword at the start of a function Async/Async.sync: as a surrounding function
await: as keyword await/Async.wait as a function

async

The environment in Async.sync function have been injected some new functions for compatibility or enhancement:

  1. await: A reference of Async.wait function;
  2. pcall: Be compatible with LuaJIT;
  3. xpcall: Be compatible with LuaJIT;

async in JavaScript return Promise object only with single result, but may carry multiple results in Lua. The resolved result of Promise object return by async function will be packed into a table via {...}. However, the result handled by await will be unpacked and return multiple values.

local async = require('async')

local function f()
    return 1, 2, 3
end

-- multiple results are packed into resolved result in Promise
async(f):thenCall(function(v)
    print(v[1], v[2], v[3]) -- output: 1 2 3
end)

-- results returned by `await`
async(function()
    local v1, v2, v3 = await(async(f))
    print(v1, v2, v3) -- output: 1 2 3
end)

uv.run()

Development

Neovim tips

  • Promise.resolve():thenCall(cb) is almost equivalent to vim.schedule(cb).

Run tests

make test

Improve completion experience

Following typings/README.md

Customize EventLoop

TODO, refer to loop.lua

Credit

Feedback

  • If you get an issue or come up with an awesome idea, don't hesitate to open an issue in github.
  • If you think this plugin is useful or cool, consider rewarding it a star.

License

The project is licensed under a BSD-3-clause license. See LICENSE file for details.

promise-async's People

Contributors

kevinhwang91 avatar wookayin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

promise-async's Issues

neovim ufo broken

Version (lua -v) or (nvim -v | head -n1)

LuaJIT 2.1.1707061634

Operating system/version

windows/archlinux(wsl)

How to reproduce the issue

正常进行了插件更新

neovim为 nightly

但这个 commit 似乎破坏了什么,17ab3826b25067045bd2cdf65cd0029a26440d3f,在此之前的commit 均一切正常

导致启动 neovim 时,会导致 ufo 报告以下错误:

Error executing vim.schedule lua callback: UnhandledPromiseRejection with the reason:
.../nvim-data/lazy/nvim-ufo/lua/ufo/provider/treesitter.lua:132: UfoFallbackException
stack traceback:
           [C]:-1: in function 'error'
           .../nvim-data/lazy/nvim-ufo/lua/ufo/provider/treesitter.lua:132: in function 'mainFunc'
           .../Local/nvim-data/lazy/nvim-ufo/lua/ufo/provider/init.lua:36: in function <Anonymous:35>

Expected behavior

正常工作

Actual behavior

error

help: can I call neovim async function?

Feature description

I want change this code to wait for hover() actually work and auto hover again

            keys[#keys + 1] = {
                "gh",
                function()
                    vim.lsp.buf.hover()
                    vim.defer_fn(function()
                        vim.lsp.buf.hover()
                    end, 100)
                end,
                desc = "hover in lsp",
            }

currently I'm using stupid delay, can I use this lib to achieve synchronize?

Describe the solution you'd like

yes

Additional context

No response

Question: passing arguments to async(promise) call

Feature description

Not 100% sure if discussions are used here, so making an issue instead.


Completely unaware of how anything in Javascript work in any way, but in the example below:

local _get_branch = function()
  return promise.new(function(resolve, reject)
    require("plenary.job")
      :new({
        command = "git",
        args = { "branch", "--show-current" },
        on_exit = function(job, rc)
          if rc == 0 then
            resolve(table.concat(job:result()))
          else
            reject()
          end
        end,
      })
      :start()
  end)
end

local _merge_base = function(branch)
  return promise.new(function(resolve, reject)
    require("plenary.job")
      :new({
        command = "git",
        args = { "merge-base", branch },
        on_exit = function(job, rc)
          if rc == 0 then
            resolve(table.concat(job:result()))
          else
            reject()
          end
        end,
      })
      :start()
  end)
end

async(function()
  local branch = await(async(_get_branch))
  print(branch)
  
  -- Want to pass `branch` to `_merge_base`here. 
  local merge_base = await(async(_merge_base, branch))
  print(merge_base)
end)

how can I pass branch to _merge_base?


Looked at nvim-ufo (awesome work there by the way) as well as some of the examples here, but I do not see any examples of this, which makes me assume it is not possible/intended (and I am doing something wrong).

Describe the solution you'd like

:)

Additional context

No response

E5113: Error while calling lua chunk: /home/izelnakri/Github/promise-async/lua/async.lua:88: attempt to yield across C-call boundary

Version (lua -v) or (nvim -v | head -n1)

NVIM v0.10.0 | LuaJIT 2.1.1713773202

Operating system/version

Linux x1-carbon 6.9.6-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 21 Jun 2024 19:49:19 +0000 x86_64 GNU/Linux

How to reproduce the issue

local Promise = require("promise")
local Async = require("async")

local promise = Promise:new(function(reject, resolve)
  resolve("some result")
end)

local result = Async.wait(promise)
  vim.print("RESULT IS:")
  vim.print(result)

Expected behavior

First of all, thank you for building this library! It seems well written in terms of test coverage, attention to API design etc. However when I use the promise API today in neovim I get this error:

E5113: Error while calling lua chunk: /home/izelnakri/Github/promise-async/lua/async.lua:88: attempt to yield across C-call boundary

Maybe it requires me to wrap stuff in weird ways etc. However this apparently can be prevented:

When I write a dummy Promise module that uses lua coroutines inside I do not get this error:

local Promise = {
  new = function(executor)
    local co = coroutine.create(function()
      executor(function(a)
        coroutine.yield({ "success", a })
      end, function(a)
        coroutine.yield({ "error", a })
      end)
    end)

    return co
  end,
  await = function(co)
    local success, result = coroutine.resume(co)

    return result[2]
  end,
}

local promise = Promise.new(function(resolve, reject)
  resolve("some example")
end)

local result = Promise.await(promise)

Why do I get this error in neovim currently? Seems like we can prevent this by making some adjustments.

Actual behavior

This triggers: E5113: Error while calling lua chunk: /home/izelnakri/Github/promise-async/lua/async.lua:88: attempt to yield across C-call boundary

Plenary async library triggers similar error, however using coroutine or coroutine.yield is not the actual problem it seems, this might be a problem with some compatibility adjustments.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.