Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active January 11, 2022 01:11
Show Gist options
  • Save dfkaye/dc5c6e2d223ebc432f40c47727baaa3c to your computer and use it in GitHub Desktop.
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.
// 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
// 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