等待者模式
·
Yin灏
/**
* 等待者模式
* 通过对多个异步进程进行监听,来触发未来发生的操作。
* 用来解决那些不确定先后完成的异步逻辑
*/
// 等待者对象
var Waiter = function () {
// 注册的等待对象容器
var dfd = [],
// 成功回调方法容器,
doneArr = [],
// 失败回调方法容器
failArr = [],
// 缓存 Array 方法 slice
slice = Array.prototype.slice,
// 保存当前等待者对象
that = this;
// 监控对象
var Primise = function () {
// 监控对象是否解决成功状态
this.resolved = false;
// 监控对象是否解决失败状态
this.rejected = false;
};
// 监控对象类原型方法
Primise.prototype = {
// 解决成功
resolve: function () {
// 设置当前监控对象解决成功
this.resolved = true;
// 如果没有监控对象就取消执行
if (!dfd.length) return;
// 遍历所有注册的监控对象
for (var i = dfd.length - 1; i >= 0; i--) {
// 如果有任意一个监控对象没有被解决或者解决失败则返回
if ((dfd[i] && !dfd[i].resolved) || dfd[i].rejected) return;
// 清楚监控对象
dfd.splice(i, 1);
}
// 执行解决成功回调方法
_exec(doneArr);
},
// 解决失败
reject: function () {
// 设置当前监控对象解决失败
this.rejected = true;
// 如果没有监控对象则取消执行
if (!dfd.length) return;
// 清楚所有监控对象
dfd.splice(0);
// 执行解决成功回调方法
_exec(failArr);
},
};
// 创建监控对象
that.Deferred = function () {
return new Promise();
};
// 回调执行方法
/**
* 遍历成功或失败回调函数容器,然后依次执行内部的方法。
* @param {*} arr 数组
*/
function _exec(arr) {
var i = 0,
len = arr.length;
// 遍历回调数组执行回调
for (; i < len; i++) {
try {
// 执行回调函数
arr[i] && arr[i]();
} catch (e) {}
}
}
// 监控异步方法,参数:监控对象
that.when = function () {
// 设置监控对象
dfd = slice.call(arguments);
// 获取监控对象数组长度
var i = dfd.length;
// 向前遍历监控对象,最后一个监控对象的索引为 length - 1
for (--i; i >= 0; i--) {
// 如果不存在监控对象,或者监控对象已经解决,或者不是监控对象
if (
!dfd[i] ||
dfd[i].resolved ||
dfd[i].rejected ||
!dfd[i] instanceof Primise
) {
// 清理内存 清空当前监控对象
dfd.splice(i, 1);
}
}
// 返回等待者对象
return that;
};
// 解决成功回调函数添加方法
that.done = function () {
// 向成功回调函数容器中添加回调方法
doneArr = doneArr.concat(slice.call(arguments));
// 返回等待者对象
return that;
};
// 解决失败回调函数添加方法
that.fail = function () {
// 向失败回调函数容器中添加回调方法
failArr = failArr.concat(slice.call(arguments));
// 返回等待者对象
return that;
};
};
/**
* 尝试监控异步
*/
var waiter = new Waiter();
// 第一个彩蛋,5秒后停止
var first = (function () {
// 创建监听对象
var dtd = waiter.Deferred();
setTimeout(function () {
console.log("first finish");
// 发布解决成功消息
dtd.resolve();
}, 5000);
// 返回监听对象
return dtd;
})();
// 第二个彩蛋,10秒后停止
var second = (function () {
// 创建监听对象
var dtd = waiter.Deferred();
setTimeout(function () {
console.log("second finish");
// 发布解决成功消息
dtd.resolve();
}, 10000);
// 返回监听对象
return dtd;
})();
/**
* 最后要用等待者对象监听两个彩蛋的工作状态,并执行相应的成功回调函数与失败回调函数
*/
waiter
.when(first, second) // 监听两个彩蛋
.done(
function () {
// 添加成功回调函数
console.log("success");
},
function () {
console.log("success again");
}
)
.fail(function () {
// 添加失败回调函数
console.log("fail");
});
/*
输出结果:
first
second
success
success again
*/
/**
* 我们看到 10 秒后当第二个彩蛋结束后,我们注册的两个成功回调函数成功执行,当然如果第一个执行失败,那么我们就将执行失败回调函数。
*/
var first = (function () {
var dtd = waiter.Deferred();
setTimeout(() => {
console.log("first finish");
// 发布解决失败消息
dtd.reject();
}, 5000);
return dtd;
})();
/*
输出结果:
first
fail
second finish
*/
/**
* 封装异步请求
* 将原生的 ajax 方法封装成等待者模式,将 resolve 方法放在请求成功回调函数内调用,将 reject 方法放在请求失败回调函数中调用。
*/
// 封装 get 请求
var ajaxGet = function (url, success, fail) {
var xhr = new XMLHttpRequest();
// 创建检测对象
var dtd = waiter.Deferred();
xhr.onload = function (event) {
// 请求成功
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
success && success();
dtd.resolve();
} else {
// 请求失败
dtd.reject();
fail && fail();
}
};
xhr.open("get", url, true);
xhr.open(null);
};