概念
等待者模式或者等待者对象指的是多个异步逻辑无法确认先后执行的顺序以及时机时,而我们增加对异步逻辑的监听,当每个异步状态发生变化时,进行一次确认状态,然后根据结果来决定是否执行某动作。
我们常见常用的Promise.all对象就是等待者模式的一种最佳实践。
本文codepen地址:https://codepen.io/robinson90/pen/YzPQaoe
代码实现
let Waiter = function(){
// 定义内部的监听对象数组,成功回调数组,失败回调数组
let dfd = [],doneArr = [], failArr= [],
slice = Array.prototype.slice,that = this;
let Primise = function(){
this.resolved = false
this.rejected = false
}
Primise.prototype = {
resolve:function(){
// 设置当前对象解决
this.resolved = true
//如果没有监听对象 直接返回
if(!dfd.length){
return
}
for(let 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(){
// mark fail
this.rejected = true
// no dfd,return
if(!dfd.length){return}
// eclear all dfd
dfd.splice(0)
//执行失败回调函数
_exec(failArr)
}
}
that.Deferred = function(){
return new Primise()
}
// 执行回调函数数组
function _exec(arr){
let len = arr.length
for(let i =0;i<len;i ){
try{
let fn = arr[i]
if(fn && typeof fn === 'function'){
fn()
}
}catch(e){
}
}
}
/**
* @description 监控异步方法 参数为监听对象
*/
that.when = function(){
dfd = slice.call(arguments)
let i = dfd.length
for(;i>=0;i--){
// 当不存在 或者 已经完成或者失败 或者 不为primise的实例,直接清除监控
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
}
}
使用方式
const waiter = new Waiter()
//fn1 fn2 为两个异步执行的方法
const first = function(){
let dfd = waiter.Deferred()
setTimeout(()=>{
console.log('first finish')
dfd.resolve()
},1000)
return dfd
}()
const second = function(){
let dfd = waiter.Deferred()
setTimeout(()=>{
console.log('second finish')
dfd.resolve()
},1000)
return dfd
}()
Waiter.when(first,second).done(()=>{
}).fail(()=>{
})
轮询机制的设计
const search = function(){
window.counter =Math.random(1)
console.log('查询一次')
}
const longSearch = (fn,time)=>{
let timer = setInterval(()=>{
if(window.counter>0.8){
console.log('查询结束',window.counter)
clearInterval(timer)
return
}
fn()
},time)
}
longSearch(search,500)
ajax的轮询设计
封装一个deferred对象,然后在xhr的结果返回之后使用其回调函数。
$.ajax = function(url,success,fail){
const xhr = new XMLHttpRequest()
let dfd = waiter.Deferred();
xhr.onload = function(event){
if(xhr.status>=200){
success && success()
dfd.resolve()
}else {
dfd.reject()
fail && fail()
}
}
}
promise.all
基本思路:本身也是一个异步方法,维护所有异步方法的程度 遗留长度,维护一个结果数组,每执行一个,标记一个结果,根据策略决定,是完成全部结束还是完成一个结束,然后fullfill标记完成
promise.all 源码地址:https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/enumerator.js
小结
看完等待者模式,是不是觉得这个模式其实很常用,设计的很巧妙,在没有promise这个模型的时候,其实就已经出现类似的技术方案,不同的只是没有定制为一个技术规范。
关于我
我是一名前端Coder,热爱分享生活与技术中的经验与心得。
我是一名旅游爱好者,爱好拍摄旅途中的风景与人物。
我是一名写作狂人,基本每天都有新文章产出,笔耕不辍。
个人站点
- GitHub:http://github.com/robinson90
- codepen:https://codepen.io/robinson90
- personal blog: http://damobing.com
- yuque docs: https://www.yuque.com/robinson
- juejin blog: https://juejin.im/user/5a30ce0c51882561a20a768b
个人微信号与公众号
微信:csnikey,或者扫码加我
达摩空间订阅号:damo_kongjian,或者扫描下面的二维码