-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpromise-nodeify.js
151 lines (135 loc) · 5.43 KB
/
promise-nodeify.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
;(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.promiseNodeify = factory();
}
}(this, function() {
/** @module promise-nodeify
* @copyright Copyright 2016-2018 Kevin Locke <kevin@kevinlocke.name>
* @license MIT
*/
'use strict';
/** Function which will run with a clear stack as soon as possible.
* @private
*/
var later = typeof process !== 'undefined' && typeof process.nextTick === 'function' ? process.nextTick : typeof setImmediate === 'function' ? setImmediate : setTimeout;
/** Invokes callback and ensures any exceptions thrown are uncaught.
* @private
*/
function doCallback(callback, reason, value) {
// Note: Could delay callback call until later, as When.js does, but this
// loses the stack (particularly for bluebird long traces) and causes
// unnecessary delay in the non-exception (common) case.
try {
// Match argument length to resolve/reject in case callback cares.
// Note: bluebird has argument length 1 if value === undefined due to
// https://github.com/petkaantonov/bluebird/issues/170
// If you are reading this and want similar behavior, I'll consider it.
if (reason) {
callback(reason);
} else {
callback(null, value);
}
} catch (err) {
later(function () {
throw err;
});
}
}
/** Calls a node-style callback when a Promise is resolved or rejected.
*
* This function provides the behavior of
* {@link https://github.com/then/nodeify then <code>nodeify</code>},
* {@link
* https://github.com/cujojs/when/blob/master/docs/api.md#nodebindcallback
* when.js <code>node.bindCallback</code>},
* or {@link http://bluebirdjs.com/docs/api/ascallback.html bluebird
* <code>Promise.prototype.nodeify</code> (now
* <code>Promise.prototype.asCallback</code>)} (without options).
*
* @ template ValueType
* @param {!Promise<ValueType>} promise Promise to monitor.
* @param {?function(*, ValueType=)=} callback Node-style callback to be
* called when <code>promise</code> is resolved or rejected. If
* <code>promise</code> is rejected with a falsey value the first argument
* will be an instance of <code>Error</code> with a <code>.cause</code>
* property with the rejected value.
* @return {Promise<ValueType>|undefined} <code>undefined</code> if
* <code>callback</code> is a function, otherwise a <code>Promise</code>
* which behaves like <code>promise</code> (currently is <code>promise</code>,
* but is not guaranteed to remain so).
* @alias module:promise-nodeify
*/
function promiseNodeify(promise, callback) {
if (typeof callback !== 'function') {
return promise;
}
function onRejected(reason) {
// callback is unlikely to recognize or expect a falsey error.
// (we also rely on truthyness for arguments.length in doCallback)
// Convert it to something truthy
var truthyReason = reason;
if (!truthyReason) {
// Note: unthenify converts falsey rejections to TypeError:
// https://github.com/blakeembrey/unthenify/blob/v1.0.0/src/index.ts#L32
// We use bluebird convention for Error, message, and .cause property
truthyReason = new Error(String(reason));
truthyReason.cause = reason;
}
doCallback(callback, truthyReason);
}
function onResolved(value) {
doCallback(callback, null, value);
}
promise.then(onResolved, onRejected);
return undefined;
}
/** A version of {@link promiseNodeify} which delegates to the
* <code>.nodeify</code> method on <code>promise</code>, if present.
*
* This may be more performant than {@see promiseNodeify} and have additional
* implementation-specific features, but the behavior may differ from
* <code>promiseNodeify</code> and between Promise implementations.
*
* Note that this function only passes the callback argument to
* <code>.nodeify</code>, since additional arguments are interpreted
* differently by different libraries (e.g. bluebird treats the next argument
* as an options object while then treats it as <code>this</code> for the
* callback).
*
* @ template ValueType
* @param {!Promise<ValueType>} promise Promise to monitor.
* @param {?function(*, ValueType=)=} callback Node-style callback.
* @return {Promise<ValueType>|undefined} Value returned by
* <code>.nodeify</code>. Known implementations return the
* <code>promise</code> argument when callback is falsey and either
* <code>promise</code> or <code>undefined</code> otherwise.
*/
promiseNodeify.delegated = function nodeifyDelegated(promise, callback) {
if (typeof promise.nodeify === 'function') {
return promise.nodeify(callback);
}
return promiseNodeify(promise, callback);
};
/** Polyfill for <code>Promise.prototype.nodeify</code> which behaves like
* {@link promiseNodeify}.
*
* @ template ValueType
* @this {!Promise<ValueType>}
* @param {?function(*, ValueType=)=} callback Node-style callback.
* @return {Promise<ValueType>|undefined} <code>undefined</code> if
* <code>callback</code> is a function, otherwise a <code>Promise</code>
* which behaves like <code>promise</code> (currently is <code>promise</code>,
* but is not guaranteed to remain so).
*/
promiseNodeify.nodeifyThis = function nodeifyThis(callback) {
return promiseNodeify(this, callback);
}; // Note: This file is used directly for Node and wrapped in UMD for browser
if (typeof exports === 'object') {
module.exports = promiseNodeify;
}
return promiseNodeify;
}));