Last active
January 11, 2022 01:11
-
-
Save dfkaye/dc5c6e2d223ebc432f40c47727baaa3c to your computer and use it in GitHub Desktop.
isPromise() function checks a value is a Promise instance; isPromiseLike() checks a value conforms to the Promise API.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 9 January 2022 | |
function isPromiseLike(o) { | |
var p = new (Object(o).constructor)(() => ""); | |
return ["catch", "finally", "then"].every(name => typeof p[name] == "function") | |
&& ['all', 'allSettled', 'race', 'reject', 'resolve'].every(name => typeof p.constructor[name] == 'function') | |
} | |
function isPromise(o) { | |
return Object(o) instanceof Promise; | |
} | |
[ | |
(async function() {})(), | |
new Promise(Object), | |
(function() { | |
function F() { | |
// `new.target` is constructor-based | |
"should print false"; console.warn(new.target instanceof Promise); | |
// `this` is prototype-based | |
"should print true"; console.warn(this instanceof Promise); | |
} | |
F.prototype = Promise.resolve("inheritance") | |
return new F | |
})(), | |
Number("3"), | |
{}, | |
(function() { | |
function P(F) { | |
if (!(this instanceof P)) { | |
return new P(F) | |
} | |
F((F) => {}); | |
}; | |
['all', 'allSettled', 'race', 'reject', 'resolve'].forEach(name => P[name] = ()=> name); | |
["catch", "finally", "then"].forEach(name => P.prototype[name] = () => name); | |
return P.prototype | |
})() | |
] | |
.map(v => {console.log("promise", isPromise(v)); return v}) | |
// true true true false false false | |
.map(v => console.log("promiseLike", isPromiseLike(v))); | |
// true true true false false true | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 25 April 2020 | |
// Because https://github.com/then/is-promise/issues/13 hit hacker news because of | |
// an ESM upgrade to the package - which then brought down the internet, because | |
// it's downloaded 10+ million times a week. | |
// Here's the source as of 25 April 2020 in https://github.com/then/is-promise | |
/* | |
function isPromise(obj) { | |
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; | |
} | |
*/ | |
// That could be shortened. | |
/* | |
function isPromise(obj) { | |
return typeof Object(obj).then == 'function'; | |
} | |
*/ | |
// Now we'll go a little further, verifying the prototype API methods, | |
// AND the item's constructor/inheritance. | |
function isPromise(p) { | |
return ['catch', 'then', 'finally'].every(function(name) { | |
return typeof Object(p)[name] == 'function'; | |
}) | |
&& p instanceof Promise; // NOTE: this will fail for custom Promise libraries like bluebird.js | |
} | |
// Because a custom Promise will fail the previous, we can replace the instanceof check | |
// with an API check on the constructor. | |
function isPromiseLike(p) { | |
return ['catch', 'then', 'finally'].every(function(name) { | |
return typeof Object(p)[name] == 'function'; | |
}) | |
// for custom Promise libraries like bluebird.js | |
&& ['all', 'allSettled', 'race', 'reject', 'resolve'].every(function(name) { | |
return typeof Object(p).constructor[name] == 'function'; | |
}); | |
} | |
/* Test it out */ | |
console.group('should return true'); | |
var r = function(ok, err) { return err || ok; } | |
var p = new Promise(r); | |
console.log(isPromise(p)); | |
// true | |
/* inheritance */ | |
var q = (function(r) { | |
function F() {} | |
F.prototype = new Promise(r); | |
return new F | |
}(r)); | |
console.log(isPromise(q)); | |
// true | |
console.groupEnd(); | |
console.group('should return false'); | |
var shouldReturnFalse = [ | |
true, | |
0, | |
'a', | |
[], | |
{}, | |
{ catch() {}, finally() {}, then() {}, constructor: Promise } | |
]; | |
var result = shouldReturnFalse.some(thing => isPromise(thing)); | |
console.log(result); | |
// false | |
console.groupEnd(); | |
/** Custom impl **/ | |
console.group('custom'); | |
var facade = (function() { | |
var api = { | |
catch() {}, | |
finally() {}, | |
then() {}, | |
constructor: function(r) { | |
var o = {}; // new q.constructor(r); | |
Object.keys(api).forEach(function(key) { | |
o[key] = api[key] | |
}); | |
return o; | |
} | |
}; | |
var fn = function () {}; | |
['all', 'allSettled', 'race', 'reject', 'resolve'].forEach(function(name) { | |
api.constructor[name] = fn | |
}); | |
return api; | |
}()); | |
// It's not a platform Promise | |
console.log(isPromise(facade.constructor(r))); | |
// false | |
// But it conforms to the API | |
console.log(isPromiseLike(facade.constructor(r))); | |
// true | |
console.groupEnd(); | |
/* Should print | |
should return true | |
true | |
true | |
should return false | |
false | |
custom | |
false | |
true | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment