// Copyright 2013 Traceur Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. 'use strict'; /** * Wrap a single async function to make the callback optional and hook it to * trigger reject/resolve of the Promise, which it returns (this ignores the * async function's return value). This enables the use of await with the * wrapped function. * * @param {Function} fn Function to wrap. * @param {boolean} firstArg True if the async callback is the first argument. * @return {Function} */ function wrapFunction(fn, firstArg) { return function() { var resolve, reject; var promise = new Promise(function(res, rej) { resolve = res; reject = rej; }); var args = [].slice.call(arguments); var originalCallback = args[firstArg ? 0 : args.length - 1]; function callback(err, value) { if (originalCallback) originalCallback.apply(this, arguments); if (err) reject(err); else resolve(value); } if (typeof originalCallback !== 'function') { // Callback wasn't provided to the async function, add the custom one. originalCallback = null; if (firstArg) args.unshift(callback); else args.push(callback); } else { // Callback was provided to the async function, replace it. args[firstArg ? 0 : args.length - 1] = callback; } fn.apply(this, args); return promise; }; } /** * Wrap async functions in a module to enable the use of await. * If no function name array is provided, every function with a fnSync * variant will be wrapped. * * @param {string|Object} module The exports of the module or a string that * will be passed to require to get the module. * @param {Array.} functions Function names to wrap. * @return {object} The module. */ function wrapModule(module, functions) { if (typeof module === 'string') module = require(module); if (!functions) { for (var k in module) { // HACK: wrap all functions with a fnSync variant. if (typeof module[k] === 'function' && typeof module[k + 'Sync'] === 'function') module[k] = wrapFunction(module[k]); } } else { for (var i = 0, k; i < functions.length; i++) { var k = functions[i]; module[k] = wrapFunction(module[k]); } } return module; } /** * Wrap async functions in Node.js to enable the use of await. * * @return {void} */ function wrap() { // TODO: find and wrap everything that needs to be wrapped. wrapModule('fs'); process.nextTick = wrapFunction(process.nextTick, true); // FIXME: this would ignore the return value, making it impossible to cancel // the timeout without implementing a cancel method and using it everywhere. //global.setTimeout = wrapFunction(setTimeout, true); } exports.wrap = wrap;