Skip to main content

Implement Promise Yourself

Free2015-12-13#JS#Tool#promise polyfill#JavaScript Promise Polyfill#promise workaround#promise兼容方案#promise内部原理剖析

JS promise is just one implementation of promise A+ specification, of course you can implement it yourself, thus getting the so-called polyfill

##I. Interface Analysis

###1. Core Functions

  • resolve(value)/reject(err)

Constructor parameters resolve/reject although not static methods nor instance methods, but are data entry points, so are the core of promise API, most secrets of promise mechanism are inside, all public interfaces work on this basis

  • then(onFulfilled, onRejected)

Besides resolve/reject, then is absolutely the most important, all other instance methods/static methods are not necessary, but then is indispensable. In other words, being able to new Promise and support then, the entire promise mechanism is complete, other interfaces are just icing on the cake, making promise easier to use

###2. Instance Methods

  • catch(onRejected)

Equivalent to promise.then(null, onRejected), users can write a bit less code, meanwhile gives semantics (catch error)

###3. Static Methods

  • Promise.resolve(value)

Provide a wrapper, when unsure if value is Promise instance, can use this method to get a wrapped Promise instance, internal mechanism is relatively complex, for detailed information please refer to [6.Promise.resolve(value) Solution](/articles/完全理解 promise/#articleHeader14)

  • Promise.reject(reason)

Similarly provide a wrapper, is a shortcut for creating rejected Promise, internal mechanism is much simpler than Promise.resolve(value)

  • Promise.all(iterable)

Either all complete normally, or interrupt due to error, all unfinished ones stop

  • Promise.race(iterable)

"Race", regardless of whether result is affirmative or negative, only want the fastest

###4. Advanced Functions

  • Result auto forward throw

      // Error forward throw
      new Promise(function(resolve, reject) {reject('x')})
      .then(null)
      .then(null)
      .catch(function(err) {console.log(err)})
      // Value forward throw
      new Promise(function(resolve, reject) {resolve('x')})
      .catch(null)
      .catch(null)
      .then(function(future) {console.log(future)})
    

    Error is thrown to the final catch, as long as not "eaten" automatically forward throws, finally either be "eaten", or thrown to top level reporting error

  • Result auto outer throw

      // Outer then can get inner future and error
      new Promise(function(resolve, reject) {
          resolve(new Promise(function(resolve, reject) {
              resolve(12);
              // reject(new Error('reject in nested Promise'));
          }).then(function(future) {
              return future + 1;
          }, function(error) {
              return new Error('reject again in nested Promise');
          }));
      }).then(onFulfilled, onRejected);
    

    Outer then can get inner future and error, avoid manually maintaining state, also beneficial for centralized error handling

##II. Difficulties

The difficulty lies in promise mechanism itself is asynchronous, example as follows:

var p = new Promise(function(resolve, reject) {resolve(1)}).then(function(future) {return 2;});
// Immediately output p
console.log(p);
// Output p after 1s
setTimeout(function() {
    console.log(p);
}, 1000);

Immediately output result is:

// console
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

Result output after 1s is:

// console
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}

Under this asynchronous mechanism, return and throw both become problems, for example:

// Asynchronous
function nextTick(fn) {
    setTimeout(fn, 0);
}
// test
try {
    nextTick(function() {
        throw new Error('x');
    });
}
catch (err) {
    console.log('err: ' + err.message);
}

Result is unable to catch error, x is thrown to top level, displayed in console. Easy to think of putting try..catch inside, yes, error is indeed catched, but how to notify outer layer?

return and throw are both means of passing values, cannot be directly used in asynchronous environment, need us to implement a value passing mechanism ourselves, this is the difficulty

P.S. There's an article mentioning shortcomings of try...catch, for detailed information please check Frontend Code Exception Monitoring

##III. Project Address

Address: ayqy / myPromise - Code Hosting - Open Source China Community

Note: Current version (v0.1.0 2015/12/13) has big problems, absolutely do not use directly, because currently internal mechanism is synchronous

P.S. When version updates to usable, author will explain here, before that, recommend using more reliable taylorhakes/promise-polyfill · GitHub, very lightweight implementation

###Reference Materials

  • [Translation] We have a problem with promises: Once understand promise execution mechanism won't have these problems, no need to talk about differences between four promises, but errors mentioned in article are worth noting

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment