Js执行环境
分析同步异步之前,先了解下线程与进程
- 线程
线程是CPU调度的最小单位,是建立在进程的基础上运行的单位,共享进程的内存空间。 - 进程
进程是CPU进行资源分配的基本单位
通俗点说,进程就像工厂,线程就像工人。一个进程的内存空间是同享的,每个线程都可以用这些共享内存。一个进程由一个或多个线程构成,线程是一个进程中不同的执行路线。多进程比如听歌的同时聊微信,多线程比如一个浏览器的页面。浏览器主要有以下几种线程
- GUI渲染线程(解析html、css,渲染页面);
- JS引擎线程(执行js代码,与渲染线程互斥);
- HTTP请求线程(异步请求);
- 事件触发(点击、悬浮);
- 定时器触发
Js执行环境,众所周知Js是单线程执行的,也就是一个浏览器页面上只会有一个Js线程在执行脚本。也就是所有代码依次排列,在执行完a代码后再去执行b代码。基于这种单线程的工作流带来的的阻塞,就出现了异步编程。
异步编程
常见的异步函数——setTimeout、setInterval、ajax
异步编程方式发展
1)回调函数
function parent(callback){
setTimeout(callback,1000)
}
function son(){
console.log("回调成功")
}
parent(son())
- 可读性强,实现简单。但会陷入回调地狱,不利于维护、各模块高度耦合
2)事件监听
const ev = document.getElementById('ev');
const event = new CustomEvent('eventTest', {
detail: {
message: 'Hello World',
time: new Date(),
},
bubbles: true,
cancelable: true,
});
ev.addEventListener('eventTest', function(e) {
console.log(e);
}, );
setTimeout(function() {
ev.dispatchEvent(event); //给节点分派一个合成事件
}, 1000);
- 事件监听是以方法为中心,利用方法的挂载和监听实现点对点通信
3)发布 / 订阅
class PubSub(){
constructor(){
this.handle={}
}
//订阅
on(eventType,handle){
if(!this.handles.hasOwnPropery(eventType)){
this.handles[eventType]=[];
}
//传入的回调是函数就push到handles中
if(typepf handle=='function'){
this.handles[eventType].push({name:handle});
}else{
throw new Error("缺少回调函数")
}
//每次返回this可以链式调用
return this;
}
//发布
emit(eventType,...args){
if(this.handles.hasOwnPropery(eventType)){
this.handles[eventType].forEach((item,key,arr)=>{
item.name.apply(null,args);
})
}else{
throw new Error(`${eventType}事件未注册`);
}
return this;
}
//删除事件
off(eventType,handle){
if(!this.handles.hasOwnPropery(eventType)){
throw new Error(`${eventType}事件未注册`)
}else if(typepf handle!='function'){
throw new Error("缺少回调函数")
}else{
this.handles[eventType].forEach((item,key,arr)=>{
if(item.name==handle){
arr.splice(key,1)
}
})
}
return this;
}
}
let callback = function() {
console.log('im callback');
}
let pubsub = new PubSub();
pubsub.on('completed', (...args) => {
console.log(args.join(','));
})
pubsub.on('completed', callback);
pubsub.emit('completed', 'im anissa', '18 years old', 'like coding');
pubsub.off('completed', callback);
pubsub.emit('completed', 'im dylan', '24 years old', 'like fishing');
- 以信号为中心,每当异步事件完成,就发出相应信号。所有订阅该信号的方法,都会收到通知并触发相应的处理。
4) promise
foo.then(run());
function foo() {
return new Promise(resolve => {
setTimeout(function() {
resovle();
}, 500)
})
}
- 用链式调用解决了回调地狱的问题,但无法取消promise,错误只能由reject返回
5) async & await
// 1. 返回一个promise
// 2. 返回promise函数做await
// 3. async await成对出现
function test() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const name = 123;
console.log('aaa')
resolve(name);
})
})
}
async function func() {
let val = await test();
console.log(val);
}
func();
- async/await 代码清晰,不用链式调用,await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
6) 步进 iterator & generator
1、iterator
// 步进器的实现
function takeStep(arr) {
let _index = 0;
return {
next: function() {
return (
arr.length > _index
? {
value: arr[_index++];
} : {
done: true
}
)
}
}
}
let step = takeStep([1, 2])
console.log(step.next())//value: 1
console.log(step.next())//value: 2
console.log(step.next()) //done: true
// Es6 Symbol.iterator,同时辅助提供了for of
const makeIterator = arr => {
[Symbol.iterator]() {
let _index = 0;
return {
next() {
return (
arr.length > _index
? {
value: arr[_index++]
} : {
done: true
}
)
},
return() {
return {
done: true
}
}
}
}
}
for (let value of makeIterator([1, 2, 3])) {
console.log(value);
if (value > 1) {
break;
}
}
2、generator
Generator封装的异步函数可以暂停执行,暂停执行的地方用yield注明
function *createGenerator() {
for(let i=0;i<20;i++) {
yield i
}
}
const generator = createGenerator()
console.log(generator.next())
console.log(generator.next())
//{ value: 0, done: false }
//{ value: 1, done: false }