1

I'm doing a deep dive into ES6 native Promises. Along the way I came across some articles that quote Douglas Crockford design choices regarding not using things like new, Object.create, this, etc. Some people are advocating the use of Factory Functions over constructors. I have also learnt that there is a lot of heated debate regarding those choices. So to avoid having this question listed as not constructive, I'd like to ask this specific question.

How can I create a factory function for a Promise without using new?

// from [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)

var promise1 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

console.log(promise1);
// expected output: [object Promise]

This is also true for using things like building a new Headers() for a fetch.

At some point in the factory function, I'm going to have to write:

new Promise();

Are DC and the other authors referring to custom objects only, and not builtins? What about all the other API's that require the use of new?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
sansSpoon
  • 2,115
  • 2
  • 24
  • 43

2 Answers2

1

If you have a "class-like" function that requires you to call it with new keyword you could convert it to a factory function using Object.create function. Like this

function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = function() {
  console.log(this.name);
}

const makePerson = (name) => {
  const person = Object.create(Person.prototype);
 
  Person.call(person, name);
  
  return person;
}

makePerson('Joe').sayHi()

But this wont work with Promise because (from spec)

Promise is not intended to be called as a function and will throw an exception when called in that manner.

const makePromise = (executor) => {
  const promise = Object.create(Promise.prototype);
  
  Promise.call(promise, executor); //this line throws
  
  return promise;
}

try {
  makePromise(resolve => setTimeout(resolve, 1000, 'Hi!')).then(console.log)
} catch(e) {
  console.error('Crashed due to ', e.message)
}

Again but there is an ultimate factory that comes with Reflect API Reflect.construct. So if you want to avoid using new at all costs you could do it like this

const makePromise = (executor) => {
  return Reflect.construct(Promise, [executor], Promise)
}

makePromise(resolve => setTimeout(resolve, 1000, 'Hi!'))
  .then(console.log);
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
0

Anyway in some place you need to call the new keyword. You have already done the 99%.

function promiseFactory(bodyFunc) {
  return new Promise(bodyFunc);
}

And use this like

const promise = promiseFactory((resolve, reject) => setTimeout(resolve, 100, 'foo'));

Example

function promiseFactory(bodyFunc) {
   return new Promise(bodyFunc);
}

const promise1 = promiseFactory((resolve, reject) => setTimeout(resolve, 100, 'foo'));
const promise2 = promiseFactory((resolve, reject) => setTimeout(resolve, 500, 'boo'));

promise1.then(r => console.log(r));
promise2.then(r => console.log(r));

If you want to use already resolved or rejected Promise, you can use Promise.resolve and Promise.reject respectively.

Suren Srapyan
  • 66,568
  • 14
  • 114
  • 112