本文采用es6语法实现Promise基本的功能, 适合有javascript和es6基础的读者,若是没有,请阅读javascript
在平常开发中,一般会使用ajax请求数据,拿到数据后,对数据进行处理。java
可是,假设你须要屡次ajax请求数据,而且每次ajax请求数据都是基于上一次请求数据做为参数,再次发出请求,因而代码可能就成了这样:es6
function dataAjax() {
$.ajax({
url: '/woCms/getData',
type: 'get',
success: function (data) {
reChargeAjax(data);
}
});
}
function reChargeAjax(data) {
$.ajax({
url: '/woCms/reCharge',
type: 'post',
data: { data },
success: function (data) {
loginAjax(data);
}
});
}
....
复制代码
很明显,这样的写法有一些缺点:ajax
let pro = new Promise(function (resolve, reject) {
});
复制代码
状态只能从初始化 -> 成功或者初始化 -> 失败,不能逆向转换,也不能在成功fulfilled 和失败rejected之间转换。promise
好了,目前咱们知道了Promise的基础定义和语法,那么咱们就用代码来模拟Promise的构造函数和内部实现吧bash
class Promise {
// 构造函数,构造时传入一个回调函数
constructor(executor) {
this.status = "pending";// promise初始化状态为pending
this.value = undefined;// 成功状态函数的数据
this.reason = undefined;// 失败状态的缘由
// new Promise((resolve,reject)=>{});
let resolve = data => {
// 成功状态函数,只有当promise的状态为pending时才能修改状态
// 成功或者失败状态函数,只能调用其中的一个
if (this.status === "pending") {
this.status = "fulfilled";
this.value = data;
}
}
let reject = err => {
if (this.status === "pending") {
this.status = "rejected";
this.reason = err;
}
}
}
executor(resolve, rejcte);
}
复制代码
在Promise的匿名函数中传入resolve, rejcte这两个函数,分别对应着成功和失败时的处理,根据Promise规范,只有当Promise的状态是初始化状态是才能修改其状态为成功或者失败,而且只能转为成功或者失败。并发
在了解Promise建立和状态以后,咱们来学习Promise中一个很是重要的方法:then方法异步
then()方法:用于处理操做后的程序,那么它的语法是什么样子的呢,咱们一块儿来看下函数
pro.then(function (res) {
//操做成功的处理
}, function (error) {
//操做失败的处理
});
复制代码
那么then函数该如何定义呢?post
很明显,then属于Promise原型上的方法,接收2个匿名函数做为参数,第一个是成功函数,第二个是失败函数。
也就是说当Promise状态变为成功的时候,执行第一个函数。当状态变为失败的时候,执行第二个函数。所以then函数能够这样定义:
then(onFulFiled, onRejected) {
// promise 执行返回的状态为成功态
if (this.status === "fulfilled") {
// promise执行成功时返回的数据为成功态函数的参数
onFulFiled(this.value);
}
if (this.status === "rejected") {
// promise 执行失败是返回的缘由为失败态函数的参数
onRejected(this.reason);
}
}
复制代码
可是咱们如今来看这样一段Promise代码:
let pro = new Promise((resolve, reject) => {
setTimeout(function () {
resolve("suc");
}, 1000);
});
// 同一个promise实例屡次调用then函数
pro.then(data => {
console.log("date", data);
}, err => {
console.log("err", err);
});
pro.then(data => {
console.log("date", data);
}, err => {
console.log("err", err);
})
输出结果为
date suc
date suc
复制代码
这样Promise中异步调用成功或者失败状态函数,而且Promise实例屡次调用then方法,咱们能够看到最后输出屡次成功状态函数中的内容,而此时:
那么这块在then函数中是如何处理pending状态的逻辑呢?
采用发布订阅的方式,将状态函数存到队列中,以后调用时取出。
class Promise{
constructor(executor){
// 当promise中出现异步代码将成功态或者失败态函数封装时
// 采用发布订阅的方式,将状态函数存到队列中,以后调用时取出
this.onFuiFiledAry = [];
this.onRejectedAry = [];
let resolve = data => {
if (this.status === "pending") {
...
// 当状态函数队列中有存放的函数时,取出并执行,队列里面存的都是函数
this.onFulFiledAry.forEach(fn => fn());
}
}
let reject = err => {
if (this.status === "pending") {
...
this.onRejectedAry.forEach(fn => fn());
}
}
}
then(onFuiFiled, onRejected) {
...
if (this.status === "pending") {
this.onFuiFiledAry.push(() => {
onFuiFiled(this.value);
});
this.onRejectedAry.push(() => {
onRejected(this.reason);
});
}
}
}
复制代码
看过jQuery源码的童鞋确定都清楚,jQuery是如何实现链式调用的呢?对,就是this,jQuery经过在函数执行完成后经过返回当前对象的引用,也就是this来实现链式调用。
咱们先看看一个例子,假设Promise用this来实现链式调用,会出现什么状况呢?
let pro = new Promise(function (resolve, reject) {
resolve(123);
});
let p2 = pro.then(function () {
// 若是返回this,那p和p2是同一个promise的话,此时p2的状态也应该是成功
// p2状态设置为成功态了,就不能再修改了,可是此时抛出了异常,应该是走下个then的成功态函数
throw new Error("error");
});
p2.then(function (data) {
console.log("promise success", data);
}, function (err) {
// 此时捕获到了错误,说明不是同一个promise,由于promise的状态变为成功后是不能再修改状态
console.log("promise or this:", err);
})
复制代码
很明显,Promise发生异常,抛出Error时,p2实例的状态已是失败态了,因此会走下一个then的失败态函数,而结果也正是这样,说明Promise并非经过this来实现链式调用。
那Promise中的链式调用是如何实现的呢?
结果是,返回一个新的Promise.
then(onFuiFiled, onRejected) {
let newpro;
if (this.status === "fulfilled") {
newpro = new Promise((resolve, reject) => {
onFuiFiled(this.value);
});
}
if (this.status === "rejected") {
newpro = new Promise((resolve, reject) => {
onRejected(this.reason);
});
}
if (this.status === "pending") {
newpro = new Promise((resolve, reject) => {
this.onFuiFiledAry.push(() => {
onFuiFiled(this.value);
});
this.onRejectedAry.push(() => {
onRejected(this.reason);
});
});
}
return newpro;
}
复制代码
好了,咱们继续将目光放在then函数上,then函数接收两个匿名函数,那假设then函数返回的是数值、对象、函数,或者是promise,这块Promise又是如何实现的呢?
来,咱们先看例子:
let pro = new Promise((resolve, reject) => {
resolve(123);
});
pro.then(data => {
console.log("then-1",data);
return 1;
}).then(data => {
console.log("then-2", data);
});
复制代码
例子输出的结果是 then-1 123 then-2 1
也就是说Promise会根据then状态函数执行时返回的不一样的结果来进行解析:
那这块,咱们该如何实现呢?来,看具体代码吧
function analysisPromise(promise, res, resolve, reject) {
// promise 中返回的是promise实例自己,并无调用任何的状态函数方法
if (promise === res) {
return reject(new TypeError("Recycle"));
}
// res 不是null,res是对象或者是函数
if (res !== null && (typeof res === "object" || typeof res === "function")) {
try {
let then = res.then;//防止使用Object.defineProperty定义成{then:{}}
if (typeof then === "function") {//此时当作Promise在进行解析
then.call(res, y => {
// y做为参数,promise中成功态的data,递归调用函数进行处理
analysisPromise(promise, y, resolve, reject);
}, err => {
reject(err);
})
} else {
// 此处then是普通对象,则直接调用下个then的成功态函数并被当作参数输出
resolve(res);
}
} catch (error) {
reject(error);
}
} else {
// res 是数值
resolve(res);
}
}
then(onFuiFiled, onRejected) {
let newpro;
if (this.status === "fulfilled") {
newpro = new Promise((resolve, reject) => {
let res = onFuiFiled(this.value);
analysisPromise(newpro, res, resolve, reject);
});
}
...
return newpro;
}
复制代码
analysisPromise函数就是用来解析Promise中then函数的返回值的,在调用then函数中的状态函数返回结果值时都必需要进行处理。
如今,咱们再来看Promise的一个例子:
let pro = new Promise((resolve, reject) => {
resolve(123);
});
pro.then(data => {
console.log(1);
});
console.log(2);
复制代码
输出的结果是 2 1
so,这里先输出的是2,而then函数中的状态函数后输出1,说明同步代码先于then函数的异步代码执行
那这部分的代码咱们该如何来实现呢?
采用setTimeout函数,给then函数中的每一个匿名函数都加上setTimeout函数代码,就好比这样:
then(onFuiFiled, onRejected) {
let newpro;
if (this.status === "fulfilled") {
newpro = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let res = onFuiFiled(this.value);
analysisPromise(newpro, res, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
...
return newpro;
}
复制代码
好了,关于Promise的大体实现就先分析到这里,但愿对你们有帮助,文章中若有错误,欢迎指正
文章主要参考一下具体学习资源