![Build Status](https://travis-ci.org/ujc/wait-for-stuff.svg?branch=master)
wait-for-stuff
An extendable library that can wait for stuff to happen in a synchronous-yet-non-blocking manner.
Instead of waiting for async\await
, you can now simply wait for the following "stuff":
- time (wait for x seconds to pass)
- date (wait until
date
is reached) - predicate (wait until
prediacte
returns true) - event (wait until
event
emits) - promise (wait for
promise
to settle) - generator (wait for
generator
to fully exhaust all values) - stream (wait until
readable-stream
is fully read) - callback (wait for node-style
callback
to be called) - function (wait for custom callback
function
to be called) - yield (wait for a generator to
yield
a speific value) - value (wait for
object.property
to equal value
) - property (wait for
object.property
to exist) - array (wait for
array
to contain some value) - compose (compose a new waiter from two or more existing waiters)
- result (wait for a chain of waitables to return a non-waitable result)
Table of Contents
- Why?
- Install
- How it works
- Built-in waiters
wait.for.time()
wait.for.date()
wait.for.event()
wait.for.predicate()
wait.for.promise()
wait.for.generator()
wait.for.stream()
wait.for.callback()
wait.for.function()
wait.for.yield()
wait.for.value()
wait.for.property()
wait.for.array()
- Middleware
wait.use()
wait.alias()
- Composition
wait.compose()
wait.for.result()
- Contribute
- Test
- Related
# Why ?
Because I'm tired of waiting for await\async
, and want this code to work
(without blocking node's event-loop):
var fs = require('fs');
var wait = require('wait-for-stuff');
var myFile = fs.createReadStream('my.json');
var contents = wait.for.stream(myFile);
# Install
npm install wait-for-stuff
# How it works
Behind the scenes, wait-for-stuff
uses deasync to do it's magic.
This basically means that you can write your code in a linear, sequential manner - while still allowing async operations to complete in the background on the same execution block.
# Built-in waiters
wait-for-stuff
is designed to be middleware-oriented - which is just a fancy way of saying you can add your own "stuff" to "wait for" based on your own logic.
That said, it also comes with the following built-in waiters:
#
wait.for.time(seconds)
Waits until seconds
number of seconds pass
wait.for.time(3);
#
wait.for.date(futureDateObject)
Waits until the system time passes the date of futureDateObject
.
futureDateObject
must be a Date
object.
If futureDateObject
is configured as a date that has already passed, the waiting will simply end immediately.
var theFuture = new Date( new Date().getTime() + 5000 );
wait.for.date(theFuture);
#
wait.for.event(emitter, eventName)
Waits until emitter
emits the eventName
event.
Returns the data that the event emitted (if any).
var eventData = wait.for.event(myEmitter, 'someEvent');
#
wait.for.predicate(fn)
Waits until the predicate
function returns a truthy value.
This is useful if you need a simple mechanism to wait on your own custom application logic
var isDone = false;
setTimeout(() => isDone = true, 5000);
wait.for.predicate(() => isDone);
#
wait.for.condition
Same as wait.for.predicate
.
This is just a convenience alias in case you prefer to use the word "condition" instead of "predicate"
#
wait.for.promise(promise)
Waits until promise
is settled (either resolved or rejected).
Returns the value that the promise was settled with.
var resultOrError = wait.for.promise(new Promise(...));
#
wait.for.generator(generator)
Waits until the generator
has fully exhausted all of it's yielded values.
Returns the value that the generator function returns.
generator
can either be a generator-function, or an actuale iterable-generator
(the result of a generator-function)
function* myGeneratorFunction(){
count = 0;
while (count < 10) { yield ++count }
return 'complete!';
}
var result = wait.for.generator(myGeneratorFunction);
function* myGeneratorFunction(){
count = 0;
while (count < 10) { yield ++count }
return 'complete!';
}
var iterable = myGeneratorFunction();
var result = wait.for.generator(iterable);
#
wait.for.stream(readableStream)
Waits until readableStream
has been fully read (ended).
Returns the data that was read from the stream
(either as string
or buffer
, based on what the stream emitted as it's chunks)
var myFile = fs.createReadStream('someFile.json');
var fileContents = wait.for.stream(myFile);
#
wait.for.callback(nodeAsyncFunction, ...params)
Waits until the nodeAsyncFunction
has finished, passing to it any params
that you supply.
Returns one or more values that the callback
got as it's arguments.
If the callback got just a single value, that value will be returned by wait.for.callback()
(usually either an error object or actual data).
If the callback got more than a single value, an array-of-values is returned by wait.for.callback()
. This array-of-values filters out null
and undefined
values. The order of the items in the array is the order in which they were passed into the callback.
Also, if the after filtering for null
and undefined
values the array only contains a single element, that element is returned directly (instead of returning an array with just a single element in it).
NOTE: If you want to always get an array as the return value, use # wait.for.function()
var errOrData = wait.for.callback(fs.readFile, 'foo.json');
var errOrResultSet = wait.for.callback(moreComplexFunc, 'foo.json');
#
wait.for.function(customAsyncFunction, ...params)
Waits until the customAsyncFunction
has finished, passing to it any params
that you supply.
Unlike wait.for.callback()
, any-and-all arguments that were passed into the function will be returned as the complete resultSet
of the customAsyncFunction
.
var resultSet = wait.for.function(fs.readFile, 'foo.json');
#
wait.for.yield(generator, value)
Waits until the generator
has yielded the specified value
.
generator
can either be a generator-function, or an actuale iterable-generator
(the result of a generator-function)
function* myGeneratorFunction(){
count = 0;
while (true) { yield ++count }
}
wait.for.yield(myGeneratorFunction, 5);
function* myGeneratorFunction(){
count = 0;
while (true) { yield ++count }
}
var iterable = myGeneratorFunction();
wait.for.yield(iterable, 5);
#
wait.for.value(owner, property, valueToWaitFor)
Waits until the owner[property]
matches valueToWaitFor
.
property
must be a string
owner
must be an object
var myObject = { foo: 'bar'};
setTimeout(() => myObject.foo = '123', 5000);
wait.for.value(myObject, 'foo', '123');
#
wait.for.property(owner, property)
Waits until owner
has a property named property
property
must be a string
owner
must be an object
var myObject = {};
setTimeout(() => myObject.foo = true, 5000);
wait.for.property(myObject, 'foo');
#
wait.for.array(array, value)
Waits until array
contains value
var myArray = [];
setTimeout(() => myArray.push('hello world'), 1000);
wait.for.array(myArray, 'hello world');
# Middleware
This library aims to provide atomic structures with the built-in waiters.
From these, you can construct any custom waiter for anything additional that you may need in your application.
Once you've built your own waiter-middleware - or installed third-party waiter-middleware - you can add it to wait-for-stuff
using the wait.use(name, middleware)
api.
#
wait.use(name, middleware)
Adds middleware
as a waiter that can be used with the general wait.for...
API, under wait.for.<name>
.
var wait = require('wait-for-stuff');
wait.use('minutes', minutes => {
wait.for.seconds(minutes * 60);
return;
});
wait.for.minutes(2);
NOTE: You can also use this api to overwrite existing waiters with your own logic.
While this is possible to do, it is not recommended.
#
wait.alias(originalName, alias)
Allows you to create an alias of your own liking to an existing waiter.
For example, wait.for.condition()
is just an alias to wait.for.predicate()
.
wait.alias('promise', 'syncPromise');
wait.for.syncPromise(myPromise);
# Composition
You can compose
an advanced waiter by combining the work of two or more waiters together.
This is done using wait.compose(waiter1, waiter2, ...waiterN)
.
The result is a new waiter that passes the return value from one waiter to the next, until all waiters have completed.
#
wait.compose(waiter1, waiter2, ...waiterN)
Composes a new waiter from the waiters that are passed in.
Waiters are exhausted from right-to-left - just like you would expect from the functionl-programming compose
function
function myComplexFunction(path, callback){
fs.exists(path, result => {
var stream = fs.createReadStream(path);
callback(stream);
});
};
var streamAndCallbackWaiter = wait.compose('stream', 'callback');
var result = streamAndCallback(myComplexFunction, __filename);
#
wait.for.result(waitable))
Waits until a chain of waitables return a non-waitable result.
For example, if you need to wait on a promise that returns another promise that returns a stream - you can just wait for the result of the final stream using wait.for.result
.
waitable
can be any of the following:
- Promise
- Generator (or the iterator-result of a generaotr)
- Stream
var myComplexPromise = new Promise((res, rej) => {
setTimeout(() => {
res(new Promise((res, rej) => {
setTimeout(() => res(fs.createReadStream('someFile.json')), 500);
}));
}, 500);
});
var result = wait.for.result(myComplexPromise);
# Contribute
I hope people will find this module helpful - either as an alternative to asynchronous flow-execution patterns such as await\async
(until it's official release at least) - or as a supplement to go along with what ever you're all ready using.
If you create your own waiter-middlewares, please do share them with the community.
If you would like to have your waiter middlware added as a built-in to wait-for-stuff
, please send a PR (please also make sure to include tests)
# Test
npm run test
# Related