3

I am using async functions and default parameters with evaluation at call time.

With the default parameters I use a function to check if a value is provided or not.

function mandatory(paramName) {
    throw new Error(`Missing parameter: ${paramName}`)
}

async function foo({ a, b = mandatory('b') }) {
    return Promise.resolve(b)
}

// uses chai.assert and chai-as-promised
describe('foo', () => {
    it('should return a rejected promise', async () => {
        const promise = foo({ a: 'hi' })
        assert.isRejected(promise, /Error: Missing parameter: b/)
    })
})

This test fails with an error:

Error: Missing parameter: b

because this exception is thrown outside the async flow as you can see here:

var foo = function () {
    var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(_ref2) {
        var _ref2$a = _ref2.a,
            a = _ref2$a === undefined ? 'a' : _ref2$a,
            _ref2$b = _ref2.b,
            b = _ref2$b === undefined ? mandatory('b') : _ref2$b;
        return regeneratorRuntime.wrap(function _callee$(_context) {
            while (1) {
                switch (_context.prev = _context.next) {
                    case 0:
                        return _context.abrupt('return', Promise.resolve(b));

                    case 1:
                    case 'end':
                        return _context.stop();
                }
           }
       }, _callee, this);
    }));

    return function foo(_x) {
        return _ref.apply(this, arguments);
    };
}();

function _asyncToGenerator(fn) { 
    return function () { 
         var gen = fn.apply(this, arguments); 
         return new Promise(function (resolve, reject) { 
             function step(key, arg) { 
                 try { 
                     var info = gen[key](arg); 
                     var value = info.value; 
                 } catch (error) { 
                     reject(error); return; 
                 } 
                 if (info.done) { 
                     resolve(value); 
                 } else { 
                     return Promise.resolve(value).then(function (value) { 
                         step("next", value); 
                     }, 
                     function (err) { 
                         step("throw", err); 
                     }); 
                 } 
             } 
             return step("next"); 
         }); 
     }; 
 }

My question: is this per spec or per implementation? I would expect the promise to be rejected, not thrown.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
rolfst
  • 31
  • 3
  • I wonder how it word react with `async function foo( a, b = mandatory('b'))` with the argument being parameters instead of object properties. – bhantol Dec 02 '16 at 14:12
  • Parameter evaluation happens at function call time. The *call* to an `async` function is itself synchronous. – Pointy Dec 02 '16 at 14:14
  • async functions are part of ES 2017 draft, not ES 7. – Michał Perłakowski Dec 02 '16 at 14:21
  • @bhantol it works in the same we I've tested it. eg. not as I expected it but with a thrown error instead of a rejected promise – rolfst Dec 02 '16 at 14:57

1 Answers1

3

This is probably a bug in Babel, because in the latest Chrome (which supports async functions with a flag) and in Firefox 52, this code works as expected (i.e. the promise is rejected):

function mandatory(paramName) {
  throw new Error(`Missing parameter: ${paramName}`)
}

async function foo({ a, b = mandatory('b') }) {
  return Promise.resolve(b)
}

foo({a: 'hi'}).catch(error => console.log('rejected')) // logs 'rejected'
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177