Promise是ES6提供的原生的异步编程解决方案,它的出现主要是为了解决回调地狱实现异步编程的糟糕语法。最早是由社区提出并实现的。本文提供了一个轻量级的Promise实现方式,主要想解释下Promise实现的主要原理。至于更细节的部分,本文不做阐述。
我已经将该轻量级的Promise实现放到了Gist上面:
https://gist.github.com/licaomeng/528d0a63c3305531c5b36c138fea17dc
function MyPromise(fn) {
this.resolve;
this.reject;
_this = this;
setTimeout(function () {
fn(_this.resolveFunc.bind(_this), _this.rejectFunc.bind(_this));
}, 0)
}
MyPromise.prototype = {
then: function (resolve, reject) {
this.resolve = resolve;
this.reject = reject;
},
resolveFunc: function (value) {
this.resolve(value);
},
rejectFunc: function (value) {
this.reject(value);
}
}
new MyPromise(function (resolve, reject) {
var flag = Math.round(Math.random())
if (flag) {
resolve(flag);
} else {
reject(flag);
}
}).then(function (resolve) {
console.log(resolve);
}, function (reject) {
console.log(reject);
})
可以看到,Promise是一个容器,对异步操作进行了封装,实际上还是通过回调函数实现的异步调用。
下面就来具体解释下轻量级Promise的原理。
首先new MyPromise对象的时候传入了一个匿名函数,这个匿名函数有两个参数分别是resolve和reject,用来改变Promise的状态。
resolve: pending -> fullfilled
reject: pending -> rejected
Promise new之后 resolve和reject这两个方法会被"立即"执行,这两个函数的定义在哪里呢?在prototype里面!即 resolveFunc rejectFunc.
那这两个函数调用之后会发生什么呢?就会分别回调then方法中的两个匿名函数。
那么问题来了,刚才说了,new Promise的时候传入的匿名函数会被立即执行,而then方法是之后调用的,那么怎么才能调用到then方法中的两个匿名函数呢?这里就涉及到setTimeout调用时机的经典问题。setTimeout不在js主线程中,它由浏览单独的一个队列维护。调用的时机是,js主线程中的代码全部执行完后,再执行。所以可以看到上面的new Promise传入的方法外面包了setTimeout:
setTimeout(function () {
fn(_this.resolveFunc.bind(_this), _this.rejectFunc.bind(_this));
}, 0)
也就是等then方法中的resolve, reject 方法注册完毕后,再来进行调用。
可以看到,本质上Promise还是回调,只不过对调用方式进行了封装,让回调看上去串行了而已。