完成一个本身的Promise

媒介

在写这个promise之前,愿望你已对es6中的Promise很熟悉了,概念性和基础的东西就不再讲了,不懂的同砚可以去看看阮一峰先生的es6教程. 我主要按以下5个步骤来一步一步完成,异步的完成我放在了背面,所以前面几步暂不斟酌

  1. 完成一个基础的MyPromise
  2. 完成then的链式挪用
  3. 处置惩罚reolve函数的参数是MyPromise实例的状况以及处置惩罚then要领中前一个回调函数返回的也是一个MyPromise实例的状况
  4. 完成异步的MyPromise
  5. 完美MyPromise的别的要领

1.完成一个基础的MyPromise

/*
 * 这里我将promise的3个状况离别定义为: pending, resolved, rejected
 * 个中fn必需是个函数, 必需经由过程new来运用
 */
function MyPromise(fn) {
  if (!(this instanceof MyPromise)) {
    throw new TypeError('MyPromise must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('MyPromise constructor argument is not a function');
  }
  this.state = 'pending';  // 出初始化状况
  this.value = undefined;  // 初始化一个值, 用来存储resolve或许reject的值
  // 实行 fn 要领
  executeFn(fn, this);
}

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var res = undefined;
  var cb = this.state === 'resolved' ? onFullfilled : onRejected;
  res = cb(this.value);
}

// 实行 fn 要领
function executeFn(fn, promise) {
  var done = false;     // 声明一个变量, 防备resolve, reject一连挪用
  try {
    fn(function _resolve(value) {
      if(done) return;
      done = true;
      resolve(promise, value);
    }, function _reject(reason) {
      if(done) return;
      done = true;
      reject(promise, reason);
    });
  }
  catch(err) {
    if(!done) {
      done = true;
      reject(promise, err);
    }
  }
}

function resolve(promise, value) {
  promise.state = 'resolved';
  promise.value = value;
}

function reject(promise, error) {
  promise.state = 'rejected';
  promise.value = error;
}

如许就完成了一个基础版的MyPromise,挪用要领同Promise,以下:

new MyPromise((resolve, reject) => {
  // resolve('resolved');
  reject('rejected');
}).then(res => {
  console.log('>>> res', res);
}, err =>  {
  console.log('>>> err', err);
});

2.完成then的链式挪用

原生Promise支撑链式挪用,而且then要领会返回一个新的Promise实例,第一个回调函数完成今后,会将返回效果作为参数,传入第二个回调函数,所以我们可以修改下then要领,其他稳定

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var self = this;
  var res = undefined;
  var cb = this.state === 'resolved' ? onFullfilled : onRejected;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    try {
      res = cb(self.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });

  return newPromise;
}

如许的话,链式挪用也就完成了,测试了下,没啥题目

new MyPromise((resolve, reject) => {
  resolve('resolved');
  // reject('rejected');
}).then(res => {
  console.log('>>> res', res);
  return 'res1';
}, err => {
  console.log('>>> err', err);
  return 'err1';
}).then(res2 => {
  console.log('>>> res2', res2);
 }, err2 => {
  console.log('>>> err2', err2);
});

3. 处置惩罚reolve函数的参数是MyPromise实例的状况以及处置惩罚then要领中前一个回调函数返回的也是一个MyPromise实例的状况

resolve函数的参数除了一般的值之外,还多是另一个MyPromise实例,以下,这个时刻p1的状况决议了p2的状况

const p1 = new MyPromise(function(resolve, reject) {
  // ...
});

const p2 = new MyPromise(function(resolve, reject) {
  // ...
  resolve(p1);
});

一样,在then要领中,前一个回调函数有可以返回的也是一个MyPromise对象,这时候后一个回调函数就会守候该MyPromise对象的状况发作变化,才会被挪用,比方:

const p1 = new MyPromise(function(resolve, reject) {
  // ...
});

const p2 = new MyPromise(function(resolve, reject) {
  // ...
  resolve('p2 resolved');
});
p2.then(res1 => {
  console.log('>>> res1', res1);
  return p1;
}, err1 => {
  console.log('>>> err1', err1);
}).then(res2 => {
  console.log('>>> res2', res2);
}, err2 => {
  console.log('>>> err2', err2);
})

相识以上后,我们来完成它

function resolve(promise, value) {
  if(!handlePromise(promise, value)) return;  // 增添这行

  promise.state = 'resolved';
  promise.value = value;
}
/*
 * 增添一个函数用来处置惩罚返回值或许resolve的参数是promise的状况, 末了的返回值起个标识作用
 */
function handlePromise(promise, value) {
  if(value === promise) {
    reject(promise, 'A promise cannot be resolved with itself');
    return;
  }
  if(value && (typeof value === 'object' || typeof value === 'function')) {
    var then = value.then;
    if(typeof then === 'function') {
      executeFn(then.bind(value), promise);
      return;
    }
  }
  return true;
}
// 同时then中增添一行代码
MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var self = this;
  var res = undefined;
  var cb = this.state === 'resolved' ? onFullfilled : onRejected;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    if(self.state === 'pending') return;  // 增添这行
    try {
      res = cb(self.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });

  return newPromise;
}

到这里,就处理了以上2个题目,测试一下

const p = new MyPromise((resolve, reject) => {
  // resolve('resolve a promise');
  reject('reject a promise');
});
// resolve参数是MyPromise实例
new MyPromise((resolve, reject) => {
  resolve(p);
  // reject('rejected');
}).then(res => {
  console.log('>>> res', res);
  return 'res1';
}, err => {
  console.log('>>> err', err);
  return 'err1';
}).then(res2 => {
  console.log('>>> res2', res2);
}, err2 => {
  console.log('>>> err2', err2);
});

// then第一个要领返回的是MyPromise实例
new MyPromise((resolve, reject) => {
  resolve('resolved');
  // reject('rejected');
}).then(res => {
  console.log('>>> res', res);
  return p;
}, err => {
  console.log('>>> err', err);
  return 'err1';
}).then(res2 => {
  console.log('>>> res2', res2);
}, err2 => {
  console.log('>>> err2', err2);
});

4. 完成异步的MyPromise

经由前面3步,MyPromise的已完成了泰半,接着我们来完成异步的MyPromise. 既然是异步,我们并不知道它什么时刻完毕,然则我们可以将它的异步回调存入一个数组,待它完毕后实行它,好吧,实在就是观察者形式了

首先在组织函数中加上一句

function MyPromise(fn) {
  // ...这里省略,加上下面这行
  this.callbacks = [];     // 存储异步的回调要领
  // 实行 fn 要领
  executeFn(fn, this);
}

然后在resolve和reject要领中离别加上

function resolve(promise, value) {
  // ...这里省略,加上下面几句
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}
function reject(promise, error) {
  // ...这里省略,加上下面几句
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}

末了在then要领中,将异步的回调发存入数组中

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  // ...这里省略,稳定
  var newPromise = new MyPromise(function(_resolve, _reject) {
    // 状况为pending时,将回调存入数组,由于then中要领也是异步实行
    // 所以用setTimeout,同时直接return
    if(self.state === 'pending') {
      self.callbacks.push(function() {
        setTimeout(function() {
          // 这里须要再次推断
          cb = self.state === 'resolved' ? onFullfilled : onRejected;
          try {
            res = cb(self.value);
            _resolve(res);
          }
          catch(err) {
            _reject(err);
          }
        });
      });
      return;
    }
    // then中是异步实行
    setTimeout(function() {
      try {
        res = cb(self.value);
        _resolve(res);
      }
      catch(err) {
        _reject(err);
      }
    });
  });

  return newPromise;
}

到这里,异步的MyPromise也就完成了,then要领代码有点乱,我们整顿下

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  // 这里删除了一部份代码
  var self = this;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    if(self.state === 'pending') {
      self.callbacks.push(function() {
        // 同时将这部份的代码抽成了以下要领
        handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
      });
      return;
    }
    handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
  });

  return newPromise;
}
function handleResolved(promise, onFullfilled, onRejected, _resolve, _reject) {
  setTimeout(function() {
    var res = undefined;
    var cb = promise.state === 'resolved' ? onFullfilled : onRejected;
    // 须要对cb举行推断
    if(typeof cb !== 'function') {
      if(promise.state === 'resolved') {
        _resolve(promise.value);
      }
      else {
        _reject(promise.value);
      }
      return;
    }
    try {
      res = cb(promise.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });
}

测试以下, 固然没啥题目了

const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('resolve a promise');
    // reject('reject a promise');
  }, 2 * 1000);
});

new MyPromise((resolve, reject) => {
  resolve(p);
  // reject('rejected');
}).then(res => {
  console.log('>>> res', res);
  return 'res1';
}, err => {
  console.log('>>> err', err);
  return 'err1';
}).then(res2 => {
  console.log('>>> res2', res2);
}, err2 => {
  console.log('>>> err2', err2);
});

到这里,一个也许的MyPromise也就基础完成完成了,整顿后的完全代码以下

function MyPromise(fn) {
  if (!(this instanceof MyPromise)) {
    throw new TypeError('MyPromise must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('MyPromise constructor argument is not a function');
  }
  this.state = 'pending';  // 初始化状况
  this.value = undefined;  // 初始化一个值, 用来存储resolve或许reject的值
  this.callbacks = [];     // 存储异步的回调要领
  // 实行 fn 要领
  executeFn(fn, this);
}

MyPromise.prototype.then = function(onFullfilled, onRejected) {
  var self = this;
  var newPromise = new MyPromise(function(_resolve, _reject) {
    if(self.state === 'pending') {
      self.callbacks.push(function() {
        handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
      });
      return;
    }
    handleResolved(self, onFullfilled, onRejected, _resolve, _reject);
  });

  return newPromise;
}

function handleResolved(promise, onFullfilled, onRejected, _resolve, _reject) {
  setTimeout(function() {
    var res = undefined;
    var cb = promise.state === 'resolved' ? onFullfilled : onRejected;
    if(typeof cb !== 'function') {
      if(promise.state === 'resolved') {
        _resolve(promise.value);
      }
      else {
        _reject(promise.value);
      }
      return;
    }
    try {
      res = cb(promise.value);
      _resolve(res);
    }
    catch(err) {
      _reject(err);
    }
  });
}

// 实行 fn 要领
function executeFn(fn, promise) {
  var done = false;     // 声明一个变量, 防备resolve, reject一连挪用
  try {
    fn(function _resolve(value) {
      if(done) return;
      done = true;
      resolve(promise, value);
    }, function _reject(reason) {
      if(done) return;
      done = true;
      reject(promise, reason);
    });
  }
  catch(err) {
    if(!done) {
      done = true;
      reject(promise, err);
    }
  }
}

function resolve(promise, value) {
  if(!handlePromise(promise, value)) return;

  promise.state = 'resolved';
  promise.value = value;
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}

function reject(promise, error) {
  promise.state = 'rejected';
  promise.value = error;
  promise.callbacks.forEach(function(cb) {
    cb();
  });
}

// 用来处置惩罚返回值或许resolve的参数是promise的状况, 末了的返回值起个标识作用
function handlePromise(promise, value) {
  if(value === promise) {
    reject(promise, 'A promise cannot be resolved with itself');
    return;
  }
  if(value && (typeof value === 'object' || typeof value === 'function')) {
    var then = value.then;
    if(typeof then === 'function') {
      executeFn(then.bind(value), promise);
      return;
    }
  }
  return true;
}

5. 末了来完成下MyPromise的别的要领

MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
}

// 只需不是pending状况都邑实行
MyPromise.prototype.finally = function(cb) {
  return this.then(
    function(value) {
      return MyPromise.resolve(cb()).then(function() {
        return value;
      });
    },
    function(err) {
      return MyPromise.resolve(cb()).then(function() {
        throw err;
      });
    }
  );
}

MyPromise.resolve = function(val) {
  return new MyPromise(function(resolve, reject) {
    resolve(val);
  });
}

MyPromise.reject = function(err) {
  return new MyPromise(function(resolve, reject) {
    reject(err);
  });
}

/*
 * all要领用于将多个 MyPromise 实例,包装成一个新的 MyPromise 实例
 * 只要悉数实例都resolved,才会resolve; 只需个中一个rejected,就会reject
 * 参数可以不是数组,但必需具有 Iterator 接口, 同时内里的值可以也不是promise实例
 */
MyPromise.all = function(promiseArr) {
  var args = [].slice.call(promiseArr);

  return new MyPromise(function(resolve, reject) {
    var arr = [];
    var resolveCount = 0;
    var argsLen = args.length;
    for(var i = 0; i < argsLen; i++) {
      handle(i, args[i]);
    }
    function handle(index, val) {
      MyPromise.resolve(val).then(function(value) {
        arr[index] = value;
        if(++resolveCount === argsLen) {
          resolve(arr);
        }
      }, reject);
    }  
  });
}

/*
 * race要领与all要领相似,只需个中一个实例状况发作转变resolved / rejected即可
 * 参数可以不是数组,但必需具有 Iterator 接口, 同时内里的值可以也不是promise实例
 */
MyPromise.race = function(promiseArr) {
  var args = [].slice.call(promiseArr);
  return new MyPromise(function(resolve, reject) {
    for(var i = 0; i < args.length; i++) {
      MyPromise.resolve(args[i]).then(resolve, reject);
    }
  });
}

至此Promise的完成就算完成了,完全代码的地点点这里

末了

说点题外话,在口试的过程当中,经常会碰见口试官请求现场完成一个Promise,看了这篇文章后愿望对你有所协助,已可以完成一个Promise了,而关于口试中其他的promise的相干题目可以轻松应对

    原文作者:雪痕
    原文地址: https://segmentfault.com/a/1190000017359752
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞