一篇文章用ES6手撸一个Promise

说到Promise,都晓得它是比回调函数更优的一种异步编程处置惩罚计划,它能够使得异步操纵逻辑变得越发清楚,是处置惩罚地狱回调的一种尝试。本篇文章将会尝试用简朴易懂的言语形貌Promise的道理,而且用es6手撸一个简朴的Promise。

1. Promise的道理

道理:promise对象有三种状况,pending、fulfilled和rejected。promise对象内部保留一个须要实行一段时间的异步操纵,
当异步操纵实行完毕后能够挪用resolve或reject要领,来转变promise对象的状况,状况一旦转变就不能再变。new一个promise后
能够经由过程then要领,指定resolved和rejected时的回调函数。下面是我们一样平常运用Promise的代码逻辑。

let getAsyncData = new Promise((resolve, reject) => {
    // 实行一些异步操纵
    if (// 假如胜利) {
        // ...实行代码
        resolve();
    } else { // 假如失利
        // ...实行代码
        reject();
    }
})

);
getAsyncData.then(success, fail).then(success, fail)

连系Promise A+范例,我们就能够剖析一下我们要完成一个什么东西:

  • 完成一个状况机,有三个状况,pending、fulfilled、rejected,状况之间的转化只能是pending->fulfilled、pending->rejected,状况变化不可逆。
  • 完成一个then要领,能够用来设置胜利和失利的回调
  • then要领要能被挪用屡次,所以then要领须要每次返回一个新的promise对象,如许才支撑链式挪用。
  • 组织函数内部要有一个value值,用来保留上次实行的效果值,假如报错,则保留的是非常信息。

那我们如今就按照上面提到的道理和范例来完成这个Promise组织函数。

2. 完成

2.1 完成状况机

// promise.js
class Promise {
  constructor (executor) {
    this.status = PENDING;
    this.value = '';
    executor(this.resolve, this.reject);
  }

  resolve (value) {
    if (this.status === PENDING) {
      this.value = value;
      this.status = FULFILLED; 
    }
  }

  reject (value) {
    if (this.status === PENDING) {
      this.value = value;
      this.status = REJECTED;
    }
  }

  then (onfulfilled, onrejected) {
    if (this.status === FULFILLED) {
      onfulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onrejected(this.value);
    }
  }
}

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const test = new Promise((resolve, reject) => {
  resolve(100);
});
test.then((data) => {
  console.log(data);
},(data) => {});

由于Promise是一个组织函数,运用ES6的写法,起首想到的就是有显式constructor声明的class。上面就是我们用class的完成,能够看到如许我们就完成了这个状况机,有status, value两个属性和resolve, reject, then三个函数;同时它有pending, fulfilled和rejected三个状况,个中pending就能够切换为fulfilled或许rejected两种。

看来起还行的模样,尝试着运转一下,报错了。

ReferenceError: resolve is not defined

这是由于在class中运用this要分外警惕,类的要领内部假如含有this,它默许指向类的实例,而假如零丁运用这个要领(上面代码中的resolve(100)),this就会指向该要领运转时地点的环境,从而由于找不到这个要领而报错。所以,要在组织函数中绑定this。constructor改成

  constructor (executor) {
    this.status = PENDING;
    this.value = '';
    this.resolve = this.resolve.bind(this);
    this.reject = this.reject.bind(this);
    this.then = this.then.bind(this);
    executor(this.resolve, this.reject);
  }

再运转一下,输出了100,然则如今实在不是一个异步处置惩罚计划,代码先运转了resolve(100)然后又运转了then函数,实在关于异步的状况没有处置惩罚,不信的话就给resolve加一个setTimeout,好了,代码又没有输出了。

2.2 完成异步设置状况

作为一个异步处置惩罚的函数,在运用的时刻,我们肯定是会先设置好差别异步返回后的处置惩罚逻辑(即then的胜利、失利挪用函数),然后放心守候异步实行,末了再异步完毕后,体系会自动依据我们的逻辑挑选挪用差别回调函数。换句话说,then函数要对status为pending的状况举行处置惩罚。处置惩罚的道理是设置两个数组,在pending状况下离别保留胜利和失利回调函数,当状况转变后,再依据状况去挪用数组中保留的回调函数

我们将代码转变以下:

class Promise {
  constructor (executor) {
    this.status = PENDING;
    this.value = '';
    this.onfulfilledArr = [];
    this.onrejectedArr = [];
    this.resolve = this.resolve.bind(this);
    this.reject = this.reject.bind(this);
    this.then = this.then.bind(this);
    executor(this.resolve, this.reject);
  }

  resolve (value) {
    if (this.status === PENDING) {
      this.value = value;
      this.onfulfilledArr.forEach(item => {
        item(this.value);
      })
      this.status = FULFILLED; 
    }
  }

  reject (value) {
    if (this.status === PENDING) {
      this.value = value;
      this.onrejectedArr.forEach(item => {
        item(this.value);
      })
      this.status = REJECTED;
    }
  }

  then (onfulfilled, onrejected) {
    if (this.status === FULFILLED) {
      onfulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onrejected(this.value);
    }
    if (this.status === PENDING) {
      this.onfulfilledArr.push(onfulfilled);
      this.onrejectedArr.push(onrejected);
    }
  }
}

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const test = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(100);
  }, 2000)
});
test.then((data) => {
  console.log(data);
},(data) => {});

再运转一下,ok一般输出了。然则Promise的一大特性就是能够链式挪用,即test.then(success, fail).then(success, fail)…这就须要then返回一个新的Promise对象,而我们的顺序如今显著的是不支撑的。那末继承改一下。

2.3 完成链式挪用

再视察一下链式挪用,假如胜利和失利的函数中有返回值,这个值要作为参数传给下个then函数的胜利或失利回调。所以我们要在返回的new Promise中挪用响应的函数。

class Promise {
  constructor (executor) {
    this.status = PENDING;
    this.value = '';
    this.onfulfilledArr = [];
    this.onrejectedArr = [];
    this.resolve = this.resolve.bind(this);
    this.reject = this.reject.bind(this);
    this.then = this.then.bind(this);
    executor(this.resolve, this.reject);
  }

  resolve (value) {
    if (this.status === PENDING) {
      this.value = value;
      this.onfulfilledArr.forEach(item => {
        item(this.value);
      })
      this.status = FULFILLED; 
    }
  }

  reject (value) {
    if (this.status === PENDING) {
      this.value = value;
      this.onrejectedArr.forEach(item => {
        item(this.value);
      })
      this.status = REJECTED;
    }
  }

  then (onfulfilled, onrejected) {
    if (this.status === FULFILLED) {
      const res = onfulfilled(this.value);
      return new Promise(function(resolve, reject) {
        resolve(res);
      })
    }
    if (this.status === REJECTED) {
      const res = onrejected(this.value);
      return new Promise(function(resolve, reject) {
        reject(res);
      })
    }
    if (this.status === PENDING) {
      const self = this;
      return new Promise(function(resolve, reject) {
        self.onfulfilledArr.push(() => {
          const res = onfulfilled(self.value)
          resolve(res);
        });
        self.onrejectedArr.push(() => {
          const res = onrejected(self.value)
          reject(res);
        });
      })
    }
  }
}

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const test = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(100);
  }, 2000)
});
test.then((data) => {
  console.log(data);
  return data + 5;
},(data) => {})
.then((data) => {
  console.log(data)
},(data) => {});

再运转一下,输出100,105。好了,一个简朴的Promise就完成好了。

3. 总结

Promise实在就是对异步操纵的一种封装体式格局,能够使得回调的流程变得清楚一些,然则本质上并不处置惩罚回调地狱。由于假如有多个异步操纵嵌套,then也要一向写下去。所以厥后ES6又有了Generator,许可我们用同步的体式格局来写异步的代码,以及它的语法糖async/await,固然这就是后话了。

    原文作者:这是你的玩具车吗
    原文地址: https://segmentfault.com/a/1190000015969127
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞