0 概述
Zone.js,是一个在js环境中实现类似ThreadLocal的工具。
但能力不止这样,在SpringBoot的环境中,一条请求处理堆栈里面,他们都是同一个线程的,所以我们能轻松使用ThreadLocal来获取这条请求处理链的信息。但是,在js的环境中,请求是可能经过async/await回调,setTimeout回调,ajx回调,来完成一整套请求的,由于js环境的复杂性,所有的请求都在同一个线程中执行,不同请求的处理链也在同一个线程中执行,所以,你用全局变量是得不到同一条请求处理链的信息的。
因此,在传统的js处理链中,你要在处理链中传递信息,你就只能在所有的处理方法中都加入一个Context参数,就像golang的Ctx一样,每个请求都协商好层层透传这个Context参数。但是,这样做明显很麻烦呀,对处理方法的侵入性太大了。
Zone.js要解决的就是这个问题,解决步骤为:
- 创建一个Zone,写入属性信息,properties
- 用这个Zone的run或者runGuard方法来触发请求方法
- 那么请求方法里面,即使嵌套了async/await回调,setTimeout回调,ajx回调,后的任意闭包位置,都能通过全局Zone对象来获取这个properties!
Zone.js可以说是在库的基础上,补充了js的语法不足
Demo的代码在这里
npm install zone.js --save
安装依赖
1 properties与run
import 'zone.js';
//rootZone
console.log(Zone.current);
//在rootZone作为parent的情况下,创建normalZone
let normalZone = Zone.current.fork({
:'normalZone',
name:{
properties:'123'
fish
};
})
const go = ()=>{
//默认Zone就是root,值为undefined
console.log('go outer',Zone.current.get('fish'));
//通过normalZone.run来切换zone
.run(()=>{
normalZone//当前闭包就是在normalZone中,值为123
console.log('go inner',Zone.current.get('fish'));
;
})
}
go();
const go2 = ()=>{
//默认Zone就是root,值为undefined
console.log('go2 outer',Zone.current.get('fish'));
//通过normalZone.run来切换zone
.run(async ()=>{
normalZone//当前闭包就是在normalZone中
setTimeout(()=>{
//即使跨过了setTimeout,在另外一个调用栈中
//当前依然还是在normalZone,这就是Zone.js的关键
//不论跨过多少层setTimeout,async/await,当前Zone竟然能自动传递过去
console.log('go2 inner',Zone.current.get('fish'));
,10);
};
})
}
go2();
const delay = (timeout:number)=>{
return new Promise((resolve,reject)=>{
setTimeout(resolve,timeout);
;
})
}
async function go3_2(){
await delay(10);
console.log('go3 inner',Zone.current.get('fish'));
}
async function go3_1(){
await delay(10);
await go3_2();
}
const go3 = ()=>{
//默认Zone就是root,值为undefined
console.log('go3 outer',Zone.current.get('fish'));
//通过normalZone.run来切换zone
.run(async ()=>{
normalZone//有加await,能传递Zone
await go3_1();
//没有加await,也能传递Zone,实在屌
go3_1();
;
})
}
go3();
export default ()=>{
return <div>{'123'}</div>;
}
如代码所示了,没啥好说的
2 Zone链
import 'zone.js';
//rootZone
console.log(Zone.current);
//在rootZone作为parent的情况下,创建normalZone
let normalZone = Zone.current.fork({
:'normalZone',
name:{
properties:'123'
fish
};
})
//在normalZone作为parent的情况下,创建normalZone2
let normalZone2 = normalZone.fork({
:'normalZone',
name:{
properties:'456'
cat
};
})
const delay = (timeout:number)=>{
return new Promise((resolve,reject)=>{
setTimeout(resolve,timeout);
;
})
}
async function go4_2(){
await delay(10);
//两个属性都能拿到,这是Zone链的意义
console.log('go4 fish ',Zone.current.get('fish'));
console.log('go4 cat ',Zone.current.get('cat'));
}
async function go4_1(){
await delay(10);
await go4_2();
}
const go4 = ()=>{
//默认Zone就是root,值为undefined
console.log('go4 outer',Zone.current.get('fish'));
//通过normalZone.run来切换zone
.run(async ()=>{
normalZone2//有加await,能传递Zone
await go4_1();
;
})
}
go4();
export default ()=>{
return <div>{'123'}</div>;
}
创建Zone的时候,是需要指定parent的。所以,Zone是知道每一个Zone的整个父子链是如何构成的。当Zone去拉取属性的时候,Zone是会先到当前Zone里面拉,拉不到就往父Zone去拉,不断循环到顶部Zone的。
3 onHandleError与runGuarded
import 'zone.js';
console.log(Zone.current);
let errorZone = Zone.current.fork({
name:'errorZone',
onHandleError:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, error: any) => {
console.log('catch error ',error);
return false;
}
});
//runGuarded才能捕捉错误
//可以捕捉直接的错误
errorZone.runGuarded(()=>{
throw new Error("123");
});
const delay = (timeout:number)=>{
return new Promise((resolve,reject)=>{
setTimeout(resolve,timeout);
});
}
//可以捕捉异步错误
errorZone.runGuarded(async ()=>{
await delay(10);
throw new Error("456");
});
const myFun = async()=>{
await delay(10);
throw new Error("789");
}
//可以捕捉await异常里面的错误
errorZone.runGuarded(async()=>{
console.log('inner');
await myFun();
console.log("outer");
await myFun();
console.log("outer2");
})
const myFun2 = async()=>{
await delay(10);
throw new Error("010");
}
//可以捕捉异步,但没有await的错误
errorZone.runGuarded(async()=>{
console.log('inner2');
myFun2();
console.log("outer2");
})
export default ()=>{
return (<div>{'123'}</div>);
}
Zone可以用runGuarded与onError组合来捕获异常的,注意这种方法依然有点小问题,具体看这里Demo
4 总结
目前为止,已知Zone.js与Antd Select组件冲突,一旦打开Zone.js,Antd Select就会卡死,所以前端慎用这个库,只推荐在后端使用。
参考资料:
- 本文作者: fishedee
- 版权声明: 本博客所有文章均采用 CC BY-NC-SA 3.0 CN 许可协议,转载必须注明出处!