学会使用JavaScript的异步及优化(续)

学会使用JavaScript的异步及优化(续)

1.深入理解回调机制

回调:

特点1:函数的执行在未来的某个时刻,取决于事件函数的触发-------无法保证事件触发的顺序,js引擎只能保证触

发后函数的执行顺序,所以如何保证事件触发的顺序?(使用Promise,使用事件嵌套链式触发,使用生成器)

**特点2:**事件函数在触发之前只会通知宿主环境设置监听,引入一个延续,等待触发的执行------在此阶段产生竞争

​ 状态,先触发的先插入事件循环队列-------只有在这个阶段才能控制事件的触发顺序!!

在触发之后,环境就将回调函数插入事件执行循环队列-----等待前面事件的执行完,才会执行!------如果引

入多线程,这个时候事件的执行顺序又可能被打乱!!

**特点3:**所以在js中,代码的执行顺序分为现在执行的代码( 严格受js引擎的顺序控制 ),事件代码(控制权交托给宿

主环境提供的事件函数执行,所以想要控制顺序,必须控制事件触发函数的触发顺序!)

**特点4:**可以看出由引擎反转到事件触发函数,交托给宿主环境的第三方函数API,称为回调函数的控制反转!

如果第三方函数不够完善就会产生信任问题,这个时候就需要自己书写安全机制代码!

**特点5:**由此可以看出回调的两大缺陷,1、无法保证执行顺序(由事件触发函数控制) 2、如果事件函数API不完善

又会产生信任问题,这个时候需要自己构建安全机制代码!! 3、事件触发也并不完全都是异步的,换而言之

事件触发可能同步,可能异步,对于这种事件触发API的不确定性,带来极大的bug追踪难度!!!所以还需保证

事件触发函数都是异步的!!

function result(data){
    console.log(a)
}
var a=0
ajax("url",result)
a++
//如果ajax可能同步触发,也可能异步触发,结果是打印0还是1呢,你无法确定!!

2.异步的优化-------高级的异步模式Promise精确控制事件循环的调度执行!!

Promise:

**特点1:**由于事件触发函数将控制反转,如果我们不把控制权交给第三方,而是让第三方提供何时结束的信息(返回

未来值,或者返回错误值),然后利用这个信息执行下一步的函数执行!

function third_party(x){
    return message
}
var decision=third_party(42)
decision
    .then("success",function(){...})
	.then("failure",function(){...})    
//好处:实现第三方和己方函数的分离,不需要互相关注实现细节,己方只需要知道第三方的输入接口和输出返回
//第三方也不需要关注己方函数的执行细节,只需要返回正确的数据即可,而不需要再像使用回调时还需要维护一
//个回调函数的传入!!!
function third_party(x){
    return new Promise(function(res,rej){
        ...//使用Promise返回第三方的数据结果!
    })
}
var p=foo(42)
bar(p)
baz(p)

function bar(promise){
promise
    .then("success",function(){...})
	.then("failure",function(){...})
}
function baz(promise){
promise
    .then("success",function(){...})
	.then("failure",function(){...})
} 
//鸭子类型检查,如果使用prototype将一个不是Promise的值注册了一个then函数,就会被误认为是一个
//Promise,如果一旦误用,就会出现bug!!          

Promise如何解决回调的顺序性和可信任缺陷问题?

一、Promise建立信任机制

**解决1:**回调过早或者过晚------通过在第三方函数返回一个异步封装的Promise值,即使第三方立即被调用,

then()注册的函数也会被异步执行;;此外无论过晚执行,都没关系,then注册函数的执行取决于合适返回

Promise决议,都会在下一个异步点调用,也就是说,通过Promise决议和then机制,使得函数的执行权回到

了js引擎上

**解决2:**Promise永远为决议被挂住------使用Promise.race()能够将多个Promise实例包装新的Promise返回,率先决

议值便会返回给新Promise设置超时机制,如果指定时间未执行,Promise就会变为reject状态

**解决3:**回调次数太少或者太多------由于Promise只能被决议一次,如果resolve,reject被调用多次,Promise也

只会接受第一次决议,忽略之后的调用;;但是可以对同一个Promise决议,调用多个p.then();p.then()

**解决4:**未能传入参数、环境值-------Promise决议的resolve和reject只能接受一个参数,其后的传入参数都会被忽

略,要传入多个参数,只能封装为单个值对象传入!!

**解决5:**吞掉error-------在Promise创建时出现js异常error,Promise依然会决议,但是会决议成reject状态,而非

中断,执行同步代码;;如果实在决议之后的then(),则会直接返回一个带有reject决议的Promise,但是对于

当前的Promise就会出现吞掉错误异常的现象!!

**解决6:**返回的Promise决议本身是真的、可信任的Promise吗-----使用Promise.resolve()对传入的值进行封装

以此来获得信任,如果是非Promise值,则使用.then就会解封得到非Promise值,如果是真正的Promise,就能如

期执行,可见使用Promise.resolve实现一个过滤,建立信任机制的作用!!!

二、Promise模式

模式1:this-then-this-then...链式执行模式------这种模式特点在于任意时刻只能执行一个异步任务,而且

是顺序执行

模式2:Promise.all([...])门gate模式-------通过all设置一个门机制,只有等待多个并行/并发的异步任务执行

完成才能继续执行,但是不在乎他们的执行顺序!!

var p1=request("url1")
var p2=request("url2")
               
Promise.all([p1,p2])//all只有所有Promise完成才会返回所有的返回值数组消息msg
    .then(function(msg){
    	return request("....")
	})
    .then(function(msg){
   		console.log(msg)
	})

模式3:Promise.race([...])竞态模式-------通过race,率先决议的Promise则返回给新的Promise值,如果是

空数组就会永久挂起!!

变体模式:none([]) /any([]) /first([]) /last([])

并发迭代模式:map()并发迭代模式------对于同步的数组任务,可以使用forEach为每个任务添加额外的任务,但

是对于多个异步执行任务无法实现,这个时候必须使用异步工具map()接受一个异步任务数组,映射完任务返回

异步任务结果数组

var p1=Promise.resolve(21)
var p2=Promise.resolve(31)
var p3=Promise.reject(41)
Promise.map([p1,p2,p3],function(pr,done){
    Promise.resolve(pr)
        .then(function(v){
                done(v*2)
            },done)
})
 	.then(function(vals){
   		 console.log(vals)//[42,62,41]    
	})

三、Promise的局限性

单决议性、

单一值性、

吞掉错误、

一旦创建Promise无法取消、

由于回调代码一直保持活跃即使不能执行的异步任务,导致垃圾无法回收!

与裸露的回调函数相比执行效率较慢

缺点:相比回调函数的一团乱麻,Promise的this-then-this-then....更加的表达清晰,但是仍然有大

量的重复代码,使用生成器可以提升巨大的优美模式

3.更加高级的异步模式生成器------解决js事件循环的竞争状态

**1.认识生成器:**js引擎执行函数代码时,一旦开始执行,就会运行到结束,期间无法打断!如果需要打断,可以使

用多线程机制或者ES6引入生成器

var x=1
function *foo(){//表示引入生成器
    x++
    yield //表示函数执行的暂停
    console.log("x:",x)
}
function bar(){
    x++
}
//开始构造一个迭代器it,用于控制函数生成器*foo()
var it=foo()
it.next()//启动生成器
bar()//在暂停处执行其他代码
it.next()//再次运行生成器,并调用结束!

**2.生成器的强大作用:**不仅能够接受参数,提供返回值,还能够提供强大的内建消息的输入和输出能力;;此外

还能够对同一个生成器函数创建多个迭代器实例,或者使用多个迭代器实例交替运行多个生成器(在同一个作用域)

的情况下!!记住next()的调用次数始终比yield多一个!!使用生成器机制可以模拟多线程执行的机制!!

function *foo(x){
    var y=x*(yield)
    y
    return y
}
var it=foo(6)
it.next()
var res=it.next(7)//返回的是一个对象,通过使用yield和next在赋值表达式处暂停,并输入内建参数!!
console.log(res)
//第一个next执行到yield处,第二个next完成被暂停的表达式结果,并继续执行到结束,或者遇到第二个yield

**3.生产者和迭代器:**如果想要生产一系列的值,且每一个值都与前一个相关,也就是说每次生产当前值时,必须

要记住前面的最后值,而使用闭包函数的缓存机制就能完成这一项功能!

next()迭代器返回一个对象,{value:undefined,done:true}表示value获得yield值或者返回值

否则为undefineddone表示调用是否完成,只有最后一个next()才表示完成,前面的next()调用均是

flase

	var dosomething=(function(){
    var val //由于闭包函数的特性,每次执行时,val都会保存上一次调用执行的值,而且不会被垃圾回收!
    return function(){
        if(val===undefined){
            val=1
        }else{
            val=3*val+6
        }
        return val
        
    }
})()
dosomething()//1
dosomething()//9
dosomething()//33
dosomething()//105

var dosomething=(function(){
    var val //由于闭包函数的特性,每次执行时,val都会保存上一次调用执行的值,而且不会被垃圾回收!
    return {
    [Symbol.iterator]:function(){return this},//为了每次调用next返回一个此对象的迭代器
    next:function(){
            if(val===undefined){
                val=1
            }else{
                val=3*val+6
            }           
        	return {value:val,done:false}//返回一个对象
        }
    }
})()
//使用自定义的迭代器,进行一系列的生产值的生产!!
dosomething.next()//1
dosomething.next()//9
dosomething.next()//33
dosomething.next()//105


//使用for...of迭代器循环调用对象的next()!!
for(var v of dosomething){
    console.log(v)
    if(v>500)
        break;//必须设置便捷条件,由于迭代器默认为false,会一直循环,知道done为true
}
//除了为对象自定义迭代器,9大内建对象在ES6中也有自己的默认迭代器
var a=[1,3,5,7,8,9,10]
for( var v of a){//v是取出value值而非返回的对象
    console.log(v)
}

**4.生成器和迭代器:**由于定义一个生成器,调用生成器本身就是得到一个迭代器,意味着可以直接使用此迭代器

搭配for...of生产一系列的值,而且生成器函数的作用域会一直保持,就像相当于闭包函数,但又由于yield

中断,又不会一直锁住宿主环境,可谓一举两得!!

function *dosome(){
	var val
	while(true){
		if(val===undefined)
			val=1;
		else
			val=3*val+6;
		yield val       //通过yield拿到每次循环调用的值
	}
}
for(var v of dosome()){
	console.log(v)
	if(v>5000)
		break;
}

5.使用生成器和迭代器:实现看似同步实则异步的任务执行-----比回调和Promise更高级的模式!!

//使用看似同步的代码拿到异步的数据!!
function *foo(){
    var data=yield ajax("url",function(err,data){
        if(err){
            it.throw(err)//还可以直接同步错误处理
        }else{
            it.next(data)//一旦ajax拿到响应数据,就会调用ajax的回调函数,使用it.next()迭代器
                      //恢复暂停的生成器的执行,并返回data,从而通过暂停的方式拿到异步数据
        }
    })
    console.log(data)
}
var it=foo()
it.next()//启动生成器!!


var data=ajax("url",function(){...})
console.log(data)//比较二者的区别!!
//前者在执行异步任务之前直接中断,后者无法同步代码拿到异步任务的数据!!

6.使用生成器+Promise建立可信任和可组合机制

/*
	1.通过在生成器中yield一个Promise,通过生成器实现同步机制,再通过Promise实现可信任和组合机制,		从而完美的解决回调的两大缺陷
*/
function *foo(){
    try{
        var data=yield function(){
        	return new Promise()
   		 }
    }catch(err){
		console.log(err)
    }   
}
var it=foo()
var p=it.next().value//启动生成器!!
p
    .then(function(data){
    	it.next(data)
	},
      function(err){
    	it.throw(err)
	})

//使用封装运行Promise+生成器的工具函数
function run(generator){
    var args=[].slice.call(arguments,1),it;
    it=generator.apply(this,args)//初始化迭代器,并设置为只能接受一个参数
    return Promise.resolve().then(function handleNext(value){
        var next=it.next(value)
        return (function handleRes(next){
            if(next.done){
                return next.value
            }else{
               	return Promise.resolve(next.value)
                    .then(handleNext,function handleErr(err){
                    	return Promise.resolve(it.throw(err)).then(handleRes)
                })
            }
        })(next)
    })
}
run(foo)//直接调用此工具即可!!

7.生成器模式:

**生成器+Promise并发模式:**由于yield只能在单个点设置暂停,无法控制多个异步任务的并发进行,所以必须通过

Promise的并发机制来实现!!

function *foo(){
    var p1=request("url1")
    var p2=request("url2")//通过Promise的决议在下一个异步点执行机制实现两个ajax请求并发进行!
    
    var r1=yield p1
    var r2=yield p2
    var r3=yield request(...)
}
run(foo)

**生成器委托模式:**即在一个生成器中调用另一个生成器

function *foo(){
    console.log("*foo()starting")
    yield "f1"
    yield "f2"
    console.log("*foo()finished")
}
function *bar(){
    yield "b1"
    yield "b2"
    yield *bar()//yield委托
    yield "b3"
}
var it=bar()
console.log(
it.next(),
it.next(),

it.next(),//foo()启动
it.next(),//foo()结束

it.next(),
it.next()
)


生成器自身并发模式:

4.try-catch同步错误处理机制无法处理异步代码,必须通过生成器提供环境支持

function foo(){
    setTimeout(function(){
        baz()
    },1000)
}

try{
    foo()
}catch(err){
    //永远无法执行到这里!!
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值