diff --git a/index.js b/index.js index 51649bd..84b9803 100644 --- a/index.js +++ b/index.js @@ -343,7 +343,7 @@ if (instrumentPromise) { * The async boundary in promises that must be patched is between the * fulfillment of the promise and the execution of any callback that is waiting * for that fulfillment to happen. This means that we need to trigger a create - * when accept or reject is called and trigger before, after and error handlers + * when resolve or reject is called and trigger before, after and error handlers * around the callback execution. There may be multiple callbacks for each * fulfilled promise, so handlers will behave similar to setInterval where * there may be multiple before after and error calls for each create call. @@ -351,8 +351,8 @@ if (instrumentPromise) { * async-listener monkeypatching has one basic entry point: `wrapCallback`. * `wrapCallback` should be called when create should be triggered and be * passed a function to wrap, which will execute the body of the async work. - * The accept and reject calls can be modified fairly easily to call - * `wrapCallback`, but at the time of accept and reject all the work to be done + * The resolve and reject calls can be modified fairly easily to call + * `wrapCallback`, but at the time of resolve and reject all the work to be done * on fulfillment may not be defined, since a call to then, chain or fetch can * be made even after the promise has been fulfilled. To get around this, we * create a placeholder function which will call a function passed into it, @@ -364,14 +364,14 @@ if (instrumentPromise) { * different ways depending on the return value of the callback. When the * callback return a Promise, the new Promise is resolved asynchronously after * the returned Promise has been also been resolved. When something other than - * a promise is resolved the accept call for the new Promise is put in the + * a promise is resolved the resolve call for the new Promise is put in the * microtask queue and asynchronously resolved. * * Then must be wrapped so that its returned promise has a wrapper that can be * used to invoke further continuations. This wrapper cannot be created until * after the callback has run, since the callback may return either a promise * or another value. Fortunately we already have a wrapper function around the - * callback we can use (the wrapper created by accept or reject). + * callback we can use (the wrapper created by resolve or reject). * * By adding an additional argument to this wrapper, we can pass in the * returned promise so it can have its own wrapper appended. the wrapper @@ -379,7 +379,7 @@ if (instrumentPromise) { * value. If a promise is returned, the new Promise can proxy the returned * Promise's wrapper (this wrapper may not exist yet, but will by the time the * wrapper needs to be invoked). Otherwise, a new wrapper can be create the - * same way as in accept and reject. Since this wrapper is created + * same way as in resolve and reject. Since this wrapper is created * synchronously within another wrapper, it will properly appear as a * continuation from within the callback. */ @@ -412,15 +412,15 @@ function wrapPromise() { return promise; - function wrappedExecutor(accept, reject) { + function wrappedExecutor(resolve, reject) { context = this; args = [wrappedAccept, wrappedReject]; // These wrappers create a function that can be passed a function and an argument to - // call as a continuation from the accept or reject. + // call as a continuation from the resolve or reject. function wrappedAccept(val) { ensureAslWrapper(promise, false); - return accept(val); + return resolve(val); } function wrappedReject(val) { @@ -433,14 +433,25 @@ function wrapPromise() { util.inherits(wrappedPromise, Promise); wrap(Promise.prototype, 'then', wrapThen); + // Node.js = 6) { - children.push({ - name: 'accept from nextTick', - children: [], - before: 0, - after: 0, - error: 0 - }) - } - children.push( - { - name: 'setTimeout in 2nd chain', - children: [], - before: 1, - after: 0, - error: 0 - }, - { - name: '2nd then continuation', - children: [], - before: 0, - after: 0, - error: 0 - } - ) - - var expected = { - name: 'root', - children: [ - { - name: 'accept', - children: [ - { - name: 'nextTick in nested promise', - children: [ - { - name: 'accept from nextTick', - children: children, - before: children.length - 1, - after: children.length - 1, - error: 0 - } - ], - before: 1, - after: 1, - error: 0 - } - ], - before: 1, - after: 1, - error: 0 - }, - { - name: 'accept', - children: [], - before: 1, - after: 1, - error: 0 - }, - { - name: 'accept', - children: [], - before: 1, - after: 1, - error: 0 - } - ], - before: 0, - after: 0, - error: 0 - } -}); - test('then chain with rejected promise', function(t) { var listener = addListner(); @@ -1522,14 +1067,14 @@ test('then chain with rejected promise', function(t) { } }); -test('multi chain with rejected promise', function(t) { +test('multi catch with promise', function(t) { var listener = addListner(); listener.currentName = 'reject'; var promise = Promise.reject(10); promise - .chain(fail, function(val) { + .catch(function(val) { return new Promise(function wait(accept, reject) { listener.currentName = 'nextTick in nested promise'; process.nextTick(function() { @@ -1538,11 +1083,11 @@ test('multi chain with rejected promise', function(t) { }); }); }) - .chain(fail, function validate(val) { + .catch(function validate(val) { t.strictEqual(val, 10); t.strictEqual(this, global); - listener.currentName = 'setTimeout in 2nd chain'; + listener.currentName = 'setTimeout in 2nd catch'; setTimeout(function() { // some version of iojs use nextTick for some parts of its async if (listener.root.children.length === 2) { @@ -1559,14 +1104,9 @@ test('multi chain with rejected promise', function(t) { t.end(); }); - listener.currentName = '2nd chain continuation'; + listener.currentName = '2nd catch continuation'; }); - function fail() { - t.fail('should not be called'); - t.end(); - } - process.removeAsyncListener(listener.listener); // Promise resolution changed slightly in node v6, @@ -1583,14 +1123,14 @@ test('multi chain with rejected promise', function(t) { } children.push( { - name: 'setTimeout in 2nd chain', + name: 'setTimeout in 2nd catch', children: [], before: 1, after: 0, error: 0 }, { - name: '2nd chain continuation', + name: '2nd catch continuation', children: [], before: 0, after: 0, @@ -1624,13 +1164,6 @@ test('multi chain with rejected promise', function(t) { after: 1, error: 0 }, - { - name: 'reject', - children: [], - before: 0, - after: 0, - error: 0 - }, { name: 'reject', children: [], @@ -1638,13 +1171,6 @@ test('multi chain with rejected promise', function(t) { after: 1, error: 0 }, - { - name: 'reject', - children: [], - before: 0, - after: 0, - error: 0 - }, { name: 'reject', children: [], @@ -1659,165 +1185,47 @@ test('multi chain with rejected promise', function(t) { } }); -test('multi catch with promise', function(t) { +test('throw in executor', function(t) { var listener = addListner(); - listener.currentName = 'reject'; - var promise = Promise.reject(10); - - promise - .catch(function(val) { - return new Promise(function wait(accept, reject) { - listener.currentName = 'nextTick in nested promise'; - process.nextTick(function() { - listener.currentName = 'reject from nextTick'; - reject(val); - }); - }); - }) - .catch(function validate(val) { - t.strictEqual(val, 10); - t.strictEqual(this, global); - - listener.currentName = 'setTimeout in 2nd catch'; - setTimeout(function() { - // some version of iojs use nextTick for some parts of its async - if (listener.root.children.length === 2) { - expected.children.splice(1, 0, { - name: 'reject', - children: [], - before: 1, - after: 1, - error: 0 - }) - } + var promise = new Promise(function unsafe() { + listener.currentName = 'throw'; + throw 10; + }); - t.deepEqual(listener.root, expected); - t.end(); - }); + promise.catch(function(val) { + t.equal(val, 10, 'should match thrown value') + if (listener.root.children.length === 2) { + expected.children.splice(1, 0, { + name: 'throw', + children: [], + before: 1, + after: 0, + error: 0 + }) + } - listener.currentName = '2nd catch continuation'; - }); + t.deepEqual(listener.root, expected); + t.end(); + }); process.removeAsyncListener(listener.listener); - // Promise resolution changed slightly in node v6, - // now resolve/reject wraps again on completion. - var children = [] - if (nodeVersion[0] >= 6) { - children.push({ - name: 'reject from nextTick', - children: [], - before: 0, - after: 0, - error: 0 - }) - } - children.push( - { - name: 'setTimeout in 2nd catch', - children: [], - before: 1, - after: 0, - error: 0 - }, - { - name: '2nd catch continuation', - children: [], - before: 0, - after: 0, - error: 0 - } - ) - - var expected = { - name: 'root', - children: [ - { - name: 'reject', - children: [ - { - name: 'nextTick in nested promise', - children: [ - { - name: 'reject from nextTick', - children: children, - before: children.length - 1, - after: children.length - 1, - error: 0 - } - ], - before: 1, - after: 1, - error: 0 - } - ], - before: 1, - after: 1, - error: 0 - }, - { - name: 'reject', - children: [], - before: 1, - after: 1, - error: 0 - }, - { - name: 'reject', - children: [], - before: 1, - after: 1, - error: 0 - } - ], - before: 0, - after: 0, - error: 0 - } -}); - -test('throw in executor', function(t) { - var listener = addListner(); - - var promise = new Promise(function unsafe() { - listener.currentName = 'throw'; - throw 10; - }); - - promise.catch(function(val) { - t.equal(val, 10, 'should match thrown value') - if (listener.root.children.length === 2) { - expected.children.splice(1, 0, { - name: 'throw', - children: [], - before: 1, - after: 0, - error: 0 - }) - } - - t.deepEqual(listener.root, expected); - t.end(); - }); - - process.removeAsyncListener(listener.listener); - - var expected = { - name: 'root', - children: [ - { - name: 'throw', - children: [ - ], - before: 1, - after: 0, - error: 0 - } - ], - before: 0, - after: 0, - error: 0 + var expected = { + name: 'root', + children: [ + { + name: 'throw', + children: [ + ], + before: 1, + after: 0, + error: 0 + } + ], + before: 0, + after: 0, + error: 0 } }); @@ -2174,3 +1582,619 @@ function addListner() { return false; } } + +// for the following, +// +// https://github.com/v8/v8/commits/master/src/js/promise-extra.js +// +// is helpful context -- none of these are part of ES2015 promises, and were +// set up to be removed. + +// Node.js = 6) { + children.push({ + name: 'accept from nextTick', + children: [], + before: 0, + after: 0, + error: 0 + }) + } + children.push( + { + name: 'setTimeout in 2nd chain', + children: [], + before: 1, + after: 0, + error: 0 + }, + { + name: '2nd then continuation', + children: [], + before: 0, + after: 0, + error: 0 + } + ) + + var expected = { + name: 'root', + children: [ + { + name: 'accept', + children: [ + { + name: 'nextTick in nested promise', + children: [ + { + name: 'accept from nextTick', + children: children, + before: children.length - 1, + after: children.length - 1, + error: 0 + } + ], + before: 1, + after: 1, + error: 0 + } + ], + before: 1, + after: 1, + error: 0 + }, + { + name: 'accept', + children: [], + before: 1, + after: 1, + error: 0 + }, + { + name: 'accept', + children: [], + before: 1, + after: 1, + error: 0 + } + ], + before: 0, + after: 0, + error: 0 + } + }); + + test('multi chain with rejected promise', function(t) { + var listener = addListner(); + + listener.currentName = 'reject'; + var promise = Promise.reject(10); + + promise + .chain(fail, function(val) { + return new Promise(function wait(accept, reject) { + listener.currentName = 'nextTick in nested promise'; + process.nextTick(function() { + listener.currentName = 'reject from nextTick'; + reject(val); + }); + }); + }) + .chain(fail, function validate(val) { + t.strictEqual(val, 10); + t.strictEqual(this, global); + + listener.currentName = 'setTimeout in 2nd chain'; + setTimeout(function() { + // some version of iojs use nextTick for some parts of its async + if (listener.root.children.length === 2) { + expected.children.splice(1, 0, { + name: 'reject', + children: [], + before: 1, + after: 1, + error: 0 + }) + } + + t.deepEqual(listener.root, expected); + t.end(); + }); + + listener.currentName = '2nd chain continuation'; + }); + + function fail() { + t.fail('should not be called'); + t.end(); + } + + process.removeAsyncListener(listener.listener); + + // Promise resolution changed slightly in node v6, + // now resolve/reject wraps again on completion. + var children = [] + if (nodeVersion[0] >= 6) { + children.push({ + name: 'reject from nextTick', + children: [], + before: 0, + after: 0, + error: 0 + }) + } + children.push( + { + name: 'setTimeout in 2nd chain', + children: [], + before: 1, + after: 0, + error: 0 + }, + { + name: '2nd chain continuation', + children: [], + before: 0, + after: 0, + error: 0 + } + ) + + var expected = { + name: 'root', + children: [ + { + name: 'reject', + children: [ + { + name: 'nextTick in nested promise', + children: [ + { + name: 'reject from nextTick', + children: children, + before: children.length - 1, + after: children.length - 1, + error: 0 + } + ], + before: 1, + after: 1, + error: 0 + } + ], + before: 1, + after: 1, + error: 0 + }, + { + name: 'reject', + children: [], + before: 0, + after: 0, + error: 0 + }, + { + name: 'reject', + children: [], + before: 1, + after: 1, + error: 0 + }, + { + name: 'reject', + children: [], + before: 0, + after: 0, + error: 0 + }, + { + name: 'reject', + children: [], + before: 1, + after: 1, + error: 0 + } + ], + before: 0, + after: 0, + error: 0 + } + }); +}