Generator-based async control flow library for Node.js and browsers using promises
npx @tessl/cli install tessl/npm-co@4.6.0co is a generator-based control flow library for Node.js and browsers that allows writing asynchronous code in a synchronous-looking style using JavaScript generators and promises. It serves as a stepping stone toward ES7 async/await syntax, providing a clean abstraction for complex asynchronous operations while maintaining backward compatibility.
npm install coconst co = require('co');ES6 modules:
import co from 'co';Alternative access patterns:
const co = require('co');
// co is also available as:
co.default(); // Same as co()
co.co(); // Same as co()const co = require('co');
// Execute a generator function that yields promises
co(function* () {
// Yield promises for async operations
const result1 = yield Promise.resolve('Hello');
const result2 = yield Promise.resolve('World');
return `${result1} ${result2}`;
}).then(function(value) {
console.log(value); // "Hello World"
}).catch(function(err) {
console.error(err.stack);
});
// Wrap a generator function to return a promise-returning function
const asyncFunction = co.wrap(function* (name) {
const greeting = yield Promise.resolve('Hello');
return `${greeting}, ${name}!`;
});
asyncFunction('Alice').then(console.log); // "Hello, Alice!"Execute generator functions or generator instances and return promises.
/**
* Execute the generator function or a generator and return a promise
* @param {GeneratorFunction|Generator} gen - Generator function or generator instance
* @param {...any} args - Additional arguments passed to generator function
* @returns {Promise} Promise that resolves with the generator's return value
*/
function co(gen, ...args);Usage Examples:
// Execute generator function
co(function* () {
const data = yield fetch('/api/data').then(res => res.json());
return data;
}).then(result => console.log(result));
// Execute with context and arguments
const result = co.call(context, function* (param1, param2) {
return yield someAsyncOperation(param1, param2);
}, 'arg1', 'arg2');
// Execute generator instance
function* myGenerator() {
return yield Promise.resolve('done');
}
co(myGenerator()).then(console.log);Wrap generator functions to return promise-returning functions.
/**
* Wrap the given generator function into a function that returns a promise
* @param {GeneratorFunction} fn - Generator function to wrap
* @returns {Function} Function that returns a promise when called
*/
co.wrap(fn);Usage Examples:
// Create reusable async function
const fetchUser = co.wrap(function* (userId) {
const user = yield fetch(`/api/users/${userId}`).then(res => res.json());
const profile = yield fetch(`/api/profiles/${user.profileId}`).then(res => res.json());
return { ...user, profile };
});
// Use the wrapped function
fetchUser(123).then(user => console.log(user));
// Access original generator function
console.log(fetchUser.__generatorFunction__); // Original generator functionco supports yielding various types of values that represent asynchronous operations:
Yield promise objects directly for async operations.
co(function* () {
const result = yield Promise.resolve('value');
const data = yield fetch('/api').then(res => res.json());
return { result, data };
});Yield functions that accept a single callback parameter (legacy support).
co(function* () {
// Thunk function
const result = yield function(callback) {
setTimeout(() => callback(null, 'delayed value'), 100);
};
return result;
});Yield arrays of yieldable values for parallel execution using Promise.all().
co(function* () {
// Execute promises in parallel
const [user, posts, comments] = yield [
fetch('/api/user').then(res => res.json()),
fetch('/api/posts').then(res => res.json()),
fetch('/api/comments').then(res => res.json())
];
return { user, posts, comments };
});Yield objects with yieldable properties for parallel execution with labeled results.
co(function* () {
// Execute promises in parallel with named results
const results = yield {
userData: fetch('/api/user').then(res => res.json()),
settings: fetch('/api/settings').then(res => res.json()),
notifications: fetch('/api/notifications').then(res => res.json())
};
// results = { userData: {...}, settings: {...}, notifications: [...] }
return results;
});Yield other generators or generator functions for delegation.
function* fetchUserData(userId) {
const user = yield fetch(`/api/users/${userId}`).then(res => res.json());
return user;
}
co(function* () {
// Yield generator function (executed with co)
const user1 = yield fetchUserData(1);
// Yield generator instance
const user2 = yield fetchUserData(2);
return [user1, user2];
});co provides comprehensive error handling through promises and try/catch blocks:
// Promise-based error handling
co(function* () {
return yield Promise.reject(new Error('Something went wrong'));
}).catch(err => {
console.error('Caught error:', err.message);
});
// Generator try/catch error handling
co(function* () {
try {
const result = yield Promise.reject(new Error('API Error'));
} catch (err) {
console.error('Handled in generator:', err.message);
return 'fallback value';
}
});
// Invalid yieldable type error
co(function* () {
try {
// This will throw a TypeError
const result = yield "invalid yieldable";
} catch (err) {
console.error(err.message);
// "You may only yield a function, promise, generator, array, or object, but the following object was passed: "invalid yieldable""
}
});--harmony flag for Node.js versions < 4.0co supports deeply nested structures of yieldable objects:
co(function* () {
const result = yield {
users: [
fetch('/api/users/1').then(res => res.json()),
fetch('/api/users/2').then(res => res.json())
],
metadata: {
count: fetch('/api/users/count').then(res => res.json()),
lastUpdated: Promise.resolve(new Date())
}
};
return result;
});co preserves the this context throughout generator execution:
const api = {
baseUrl: 'https://api.example.com',
fetchData: co.wrap(function* (endpoint) {
const url = `${this.baseUrl}${endpoint}`; // 'this' is preserved
return yield fetch(url).then(res => res.json());
})
};
api.fetchData('/users').then(console.log);