刚开始写前端的时候,处理异步请求常常用callback,简单又顺手。后来写着写着就抛弃了callback,开始用promise来处理异步问题。promise写起来确实更加优美,但因为缺少对它内部结构的深入认识,每次在遇到一些复杂的状况时,promise用起来老是不那么驾轻就熟,debug也得搞半天。前端
因此,这篇文章我会带你们从零开始,手写一个基本能用的promise。跟着我写下来之后,你会对promise是什么以及它的内部结构有一个清楚的认知,将来在复杂场景下使用promise也能如鱼得水。segmentfault
回到正文,什么是Promise?说白了,promise就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。promise
首先,ES6规定Promise对象是一个构造函数,用来生成Promise实例。而后,这个构造函数接受一个函数(executor)做为参数,该函数的两个参数分别是resolve和reject。最后,Promise实例生成之后,能够用then方法分别指定resolved状态和rejected状态的回调函数(onFulfilled和onRejected)。缓存
具体的使用方法,用代码表现是这样:并发
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操做成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }, function(error) { // failure });
理解了这个后,咱们就能够大胆的开始构造咱们本身的promise了,咱们给它取个名字:CutePromise异步
咱们直接用ES6的class来建立咱们的CutePromise,对ES6语法还不熟悉的,能够先读一下个人另外两篇介绍ES6核心语法的文章后再回来。30分钟掌握ES6/ES2015核心内容(上)、30分钟掌握ES6/ES2015核心内容(下)函数
class CutePromise { // executor是咱们实例化CutePromise时传入的参数函数,它接受两个参数,分别是resolve和reject。 // resolve和reject咱们将会定义在constructor当中,供executor在执行的时候调用 constructor(executor) { const resolve = () => {} const reject = () => {} executor(resolve, reject) } // 为实例提供一个then的方法,接收两个参数函数, // 第一个参数函数必传,它会在promise已成功(fulfilled)之后被调用 // 第二个参数非必传,它会在promise已失败(rejected)之后被调用 then(onFulfilled, onRejected) {} }
建立了咱们的CutePromise后,咱们再来搞清楚一个关键点:Promise 对象的状态。oop
Promise 对象经过自身的状态,来控制异步操做。一个Promise 实例具备三种状态:测试
上面三种状态里面,fulfilled和rejected合在一块儿称为resolved(已定型)。状态的切换只有两条路径:第一种是从pending=>fulfilled,另外一种是从pending=>rejected,状态一旦切换就不能再改变。this
如今咱们来为CutePromise添加状态,大概流程就是:
首先,实例化初始过程当中,咱们先将状态设为PENDING
,而后当executor执行resolve的时候,将状态更改成FULFILLED
,当executor执行reject的时候将状态更改成REJECTED
。同时更新实例的value。
constructor(executor) { ... this.state = 'PENDING'; ... const resolve = (result) => { this.state = 'FULFILLED'; this.value = result; } const reject = (error) => { this.state = 'REJECTED'; this.value = error; } ... }
再来看下咱们的then函数。then函数的两个参数,onFulfilled表示当promise异步操做成功时调用的函数,onRejected表示当promise异步操做失败时调用的函数。假如咱们调用then的时候,promise已经执行完成了(当任务是个同步任务时),咱们能够直接根据实例的状态来执行相应的函数。假如promise的状态仍是PENDING, 那咱们就将onFulfilled和onRejected直接存储到chained这个变量当中,等promise执行完再调用。
constructor(executor) { ... this.state = 'PENDING'; // chained用来储存promise执行完成之后,须要被依次调用的一系列函数 this.chained = []; const resolve = (result) => { this.state = 'FULFILLED'; this.value = result; // promise已经执行成功了,能够依次调用.then()函数里的onFulfilled函数了 for (const { onFulfilled } of this.chained) { onFulfilled(res); } } ... } then(onFulfilled, onRejected) { if (this.state === 'FULFILLED') { onFulfilled(this.value); } else if (this.state === 'REJECTED') { onRejected(this.value); } else { this.$chained.push({ onFulfilled, onRejected }); } }
这样咱们就完成了一个CutePromise的建立,下面是完整代码,你们能够复制代码到控制台测试一下:
class CutePromise { constructor(executor) { if (typeof executor !== 'function') { throw new Error('Executor must be a function'); } this.state = 'PENDING'; this.chained = []; const resolve = res => { if (this.state !== 'PENDING') { return; } this.state = 'FULFILLED'; this.internalValue = res; for (const { onFulfilled } of this.chained) { onFulfilled(res); } }; const reject = err => { if (this.state !== 'PENDING') { return; } this.state = 'REJECTED'; this.internalValue = err; for (const { onRejected } of this.chained) { onRejected(err); } }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { if (this.state === 'FULFILLED') { onFulfilled(this.internalValue); } else if (this.$state === 'REJECTED') { onRejected(this.internalValue); } else { this.chained.push({ onFulfilled, onRejected }); } } }
提供一下测试代码:
let p = new CutePromise(resolve => { setTimeout(() => resolve('Hello'), 100); }); p.then(res => console.log(res)); p = new CutePromise((resolve, reject) => { setTimeout(() => reject(new Error('woops')), 100); }); p.then(() => {}, err => console.log('Async error:', err.stack)); p = new CutePromise(() => { throw new Error('woops'); }); p.then(() => {}, err => console.log('Sync error:', err.stack));
实现链式调用其实很简单,只须要在咱们定义的then()方法里返回一个新的CutePromise便可。
then(onFulfilled, onRejected) { return new CutePromise((resolve, reject) => { const _onFulfilled = res => { try { //注意这里resolve有可能要处理的是一个promise resolve(onFulfilled(res)); } catch (err) { reject(err); } }; const _onRejected = err => { try { reject(onRejected(err)); } catch (_err) { reject(_err); } }; if (this.state === 'FULFILLED') { _onFulfilled(this.internalValue); } else if (this.state === 'REJECTED') { _onRejected(this.internalValue); } else { this.chained.push({ onFulfilled: _onFulfilled, onRejected: _onRejected }); } }); }
不过,咱们还须要解决一个问题:假如then函数的第一个参数onfulfilled()自己返回的也是一个promise怎么办?好比下面这种使用方式,实际上是最真实项目场景中最多见:
p = new CutePromise(resolve => { setTimeout(() => resolve('World'), 100); }); p. then(res => new CutePromise(resolve => resolve(`Hello, ${res}`))). then(res => console.log(res));
因此咱们须要让咱们的resolve方法可以处理promise:
const resolve = res => { if (this.state !== 'PENDING') { return; } // 假如说res这个对象有then的方法,咱们就认为res是一个promise if (res != null && typeof res.then === 'function') { return res.then(resolve, reject); } ... }
以上三道思考题其实跟你用不用promise并无多大关系,可是若是你不深入理解promise想要解决这三个问题还真不是那么轻松的。