promise-async
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
promise-async/examples/demo.lua
Lines 17 to 82 in 3f6dcb2
| 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
promise-async/examples/demo.js
Lines 1 to 58 in 3f6dcb2
| 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
luarocks install promise-asyncluarocks install luvor 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:
await: A reference ofAsync.waitfunction;pcall: Be compatible with LuaJIT;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 tovim.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.