lua-promises

repository·master·Indexed 42 days ago

https://github.com/zserge/lua-promises

An A+ compliant promise implementation for Lua that allows developers to manage asynchronous operations using clean, chainable syntax. It provides the `deferred` module for creating promises, managing groups via `deferred.all`, `deferred.first`, and `deferred.map`, and converting callback-based APIs into promises.

Tokens
1.6K
Snippets
8
Records
8
Agent score
4%

What's inside lua-promises

  1. Chain promises sequentially

    master

    Promises can be chained to perform asynchronous actions one after another. If a callback in a .next() call returns a new promise, the subsequent .next() in the chain will wait for that returned promise to resolve or reject.

    This avoids the 'callback hell' of nested functions.

    read('first.txt'):next(function(s)
    	print('File file:', s)
    	return read('second.txt')
    end):next(function(s)
    	print('Second file:', s)
    end):next(nil, function(err)
    	-- error while reading first or second file
    	print('Error', err)
    end)
  2. Convert callback-based APIs to promises

    master

    You can wrap asynchronous callback-based functions into promises by following these steps:

    1. Create a new promise object using deferred.new().
    2. Start the asynchronous action.
    3. Inside the callback, call d:resolve(value) if successful, or d:reject(err) if it fails.
    4. Return the promise object so callers can chain .next() calls.

    Example:

    function read(f)
    	local d = deferred.new()
    	readasync(f, function(contents, err)
    		if err == nil then
    			d:resolve(contents)
    		else
    			d:reject(err)
    		end
    	end)
    	return d
    end
    function read(f)
    	local d = deferred.new()
    	readasync(f, function(contents, err)
    		if err == nil then
    			d:resolve(contents)
    		else
    			d:reject(err)
    		end
    	end)
    	return d
    end
    
    read('file.txt'):next(function(s)
    	print('File.txt contents: ', s)
    end, function(err)
    	print('Error', err)
    end)
  3. Install lua-promises

    master

    You can install lua-promises using LuaRocks via the following command:

    luarocks install --server=http://luarocks.org/dev lua-promises

    To use it in your Lua code, require the deferred module:

    local deferred = require('deferred')
  4. Process a list of items asynchronously with deferred.map

    master

    Use deferred.map(list, fn) to process a list of items where each item is handled by an asynchronous function fn. The items are processed sequentially: the next action starts only after the previous one completes successfully. The final promise resolves with an array containing the results of all operations.

    local items = {'a.txt', 'b.txt', 'c.txt'}
    -- Read 3 files, one by one
    deferred.map(items, read):next(function(files)
    	-- here files is an array of file contents for each of the files
    end, function(err)
    	-- handle reading error
    end)
  5. Wait for a promise using :next()

    master

    To react to a promise being resolved or rejected, use the :next(cb, [errcb]) method on the promise object.

    • cb: The resolve callback. It is called when the promise resolves. This can be nil.
    • errcb: (Optional) The rejection callback. It is called when the promise is rejected.

    This method enqueues the callbacks to be executed when the promise state changes.

    read('file.txt'):next(function(s)
    	print('File.txt contents: ', s)
    end, function(err)
    	print('Error', err)
    end)
  6. Wait for a group of promises with deferred.all

    master

    Use deferred.all(promises) to run multiple asynchronous actions in parallel and wait for all of them to finish.

    • If all promises resolve, the .next(cb) callback is called with an array of all results.
    • If at least one promise rejects, the .next(nil, errcb) callback is called with the error(s).
    deferred.all({
    	http.get('http://example.com/first'),
    	http.get('http://example.com/second'),
    	http.get('http://example.com/third'),
    }):next(function(results)
    	-- handle results here (all requests are finished and there has been no errors)
    end, function(results)
    	-- handle errors here (all requests are finished and there has been at least one error)
    end)
  7. Create and manage promises with the deferred API

    master

    The deferred module provides functions to create new promise objects and manage groups of promises.

    Creating Promises

    • deferred.new(): Returns a new promise object d.
    • deferred.all(promises): Returns a promise d that resolves when all provided promises are resolved/rejected.
    • deferred.first(promises): Returns a promise d that resolves as soon as the first of the promises gets resolved or rejected.
    • deferred.map(list, fn): Returns a promise d that resolves with the values of sequential application of function fn to each element in list. The function fn must return a promise object.

    Resolving and Rejecting

    Once you have a promise object d:

    • d:resolve(value): Resolves the promise with value.
    • d:reject(value): Rejects the promise with value.

    Note: Only the first resolution or rejection is accepted; subsequent calls are ignored.

    local deferred = require('deferred')
    local d = deferred.new()
    d:resolve('success')
    d:reject('error')
  8. Wait for the first promise to settle with deferred.first

    master

    Use deferred.first(promises) when you want to react as soon as the first promise in a group either resolves or rejects. This is useful for implementing patterns like timeouts.

    Example: Racing a file read against a timeout function.

    function timeout(sec)
    	local d = deferred.new()
    	settimeout(function()
    		d:reject('Timeout')
    	end, sec)
    	return d
    end
    
    deferred.first({
    	read(somefile),
    	timeout(5),
    }):next(function(result)
    	-- file was read successfully
    end, function(err)
    	-- either timeout or I/O error
    end)