JavaScript异步编程
从本文你将理解到
- 同步模式
Synchronous
or 异步模式Asynchronous
- 事件循环与队列音讯
- 异步编程的几种形式
- Promise异步计划,宏工作/微工作列队
- Generator异步计划,Async/Await语法糖
javascript(js)是单线程从上至下执行
同步和异步模式
同步模式
按程序排队执行,在Call stack中压入一个
anonymous
匿名调用,相当于把全副代码放在匿名函数中去顺次执行
阻塞
同步执行时某条操作比拟耗时会造成卡死
异步模式
不会期待工作完结再执行下一个工作,Callstack | WebAPIs | Event loop | Queue
代码是单线程的,浏览器不是,对于异步会产生一个异步调用线程
事件循环与队列音讯
- 代码从上至下执行,顺次押入
call stack
主栈中 - 遇到异步,主栈不会进行期待执行,会将异步工作压入浏览器开拓的另一个线程栈中运行
- 两线程并行执行
- 异步线程栈内的异步执行结束后,会将后果从异步栈中弹出至
event queue
,并期待主栈执行结束 - 当主栈执行结束,通过js外部的
event loop
事件轮询机制将监听到的曾经在event queue
队列中的异步后果安程序顺次压入主栈中执行 - 在
console
控制台输入后果
异步编程的几种形式
异步编程计划的根本就是回调函数
回调函数
相似于一件事,你明确晓得这件事怎么做,但你不晓得这件事件所依赖的工作什么时候实现,期待工作实现后调用,回调函数领有回调天堂问题
Promise
CommonJS
社区提出了Promise
标准,回调函数对立解决方案
Pending -> Fufilled -> onFufilled 异步执行胜利
-> Rejected -> onRejected 异步执行失败
Promise
异步计划,宏工作/微工作列队
一个根本的promise异步
const ps = new Promise(function (resolve,reject) {
resolve(100)
// reject(new Error("promise rejected"))
})
ps.then(function(value){ //会期待同步代码全副执行完才会执行
console.log("resolve",value)
},function(err) {
console.log("reject",err)
})
console.log("first console")
封装一个ajax
//封装一个ajax
function ajax(url){
return new Promise((res,rej)=>{
let xhr = new XMLHttpRequest()
xhr.open("GET",url)
xhr.onload = function () {
if(this.readyState==4 && this.status ==200)
{
res(this.responseText)
}
else
{
rej(new Error(xhr.statusText))
}
}
xhr.send()
})
}
ajax("/src/api/users.json").then(
res=>console.log(res),
rej=>console.log(rej)
)
然而下面的promise并不能解决回调天堂的问题
//回调天堂,减少复杂度
// ajax("/src/api/users.json").then((function(urls){
// ajax("/src/api/users.json").then((function(urls){
// ajax("/src/api/users.json").then((function(urls){
// }))
// }))
// }))
通过then办法链式调用
//通过promise的then办法的链式调用,使函数扁平化,防止回调嵌套
//then办法返回的也是个promise
//每一个then办法都是在为上一个then返回的promise对象去增加状态明确过后的回调
//then办法接管的函数的参数value 是上一个then办法的返回值,如果上一个办法无返回值,则以后value=null
//前面的then会期待后面的then(因为是个promise)完结后执行
ajax("/src/api/users.json")
.then(value=>{
console.log(111);
}) //=>Promise
.then(value=>{
console.log(222);
return ajax("/src/api/users.json") //能够手动增加个promise返回对象
}) //=>Promise
.then(value=>{
console.log(333);
})
promise异样解决
//Promise异样解决
//抛出一个异样,或者运算异样都会执行失败的回调
function ajaxError(){
return new Promise((res,rej)=>{
// foo() //增加一个不存在的办法
// throw new Error("error") //手动抛异样
//...
})
}
promise异样回调写法
//异样回调的写法
//区别,写在then里的err函数只能捕捉到第一个promise的异样,
//catch能够捕捉到链式调用中的promise异样,因为链式调用promise异样也会随上一个then返回的promise传递
//catch更像是给promise链条注册回调
ajaxError().then(
function () {
},
function () {
}
)
ajaxError().then(res=>{}).then(res=>{}).catch(err=>{})
promise静态方法
- promise.resolve()
- promise.reject()
- promise.all()
- promise.race()
//Promise.resolve(参数)
//参数是变量则包裹成一个promise对象
//参数是promise对象则被原样返回
//参数是对象,并且这个对象具备和promise一样的then办法,thenable
// 利用场景是把第三方解决异步插件的then办法转化成原生的promise对象
Promise.resolve("foo")
.then(value=>{
console.log(value) //"foo"
})
new Promise((resolve,reject)=>{
resolve("foo")
})
var promise = ajax("/src/api/users.json")
var promise2 = Promise.resolve(promise)
console.log(promise === promise2); //true
Promise.resolve({
then(onFulfilled,onRejected){
onFulfilled("foo")
}
}).then(value=>{
console.log(value) //"foo"
})
//Promise.reject()
//疾速创立一个失败的对象
//Promise.all()并行执行
//承受一个数组,数组里的每个元素是个promise对象
//当数组中promise全副执行结束,则并行异步执行完结,返回一个promise对象
//如果其中一个执行失败,那么这个promise就会以失败完结
Promise.all([
ajax("/src/api/users.json"),
ajax("/src/api/posts.json")
]).then(vals=>console.log(vals))
//利用promise对象解决ajax
- urls.json
{
"urls":"/api/users.json",
"posts":"/api/users.json"
}
- ajax.ts
ajax("/src/api/urls.json")
.then(value=>{
const urls = Object.values(value)
const tasks = urls.map(url=>ajax(url))
return Promise.all(tasks)
})
.then(vals=>console.log(vals))
//Promise.race()
//只有有一个promise对象执行实现,则进行执行返回一个new promise对象
const request = ajax("/src/api/users.json")
const settime = new Promise((res,rej)=>{
setTimeout(() => {
rej(new Error('timeout'))
}, 800);
})
Promise.race([request,settime])
.then(val=>console.log(val))
.catch(err=>console.log(err))
//如果800ms内申请到users.json那么执行then办法
//如果超时,则执行catch办法,(能够应用network online限速)
宏工作/微工作
//执行时序,宏工作微工作
console.log("start")
setTimeout(() => {
console.log("timeout")
}, 0)
Promise.resolve()
.then(()=>{
console.log('promise1');
})
.then(()=>{
console.log('promise2');
})
.then(()=>{
console.log('promise3');
})
console.log("end");
//宏工作先执行,执行完结进入回调队列的开端,
//微工作后执行,执行完结在本轮回调队列的开端立刻执行
//执行先宏后微,弹出先微后宏
//目前绝大多数异步调用都是作为宏工作执行
//Promise对象,MutationObserver对象,node中process.nextTick微工作
Generator异步计划
//promise chain
//尽管解决了回调嵌套,然而仍然无奈达到同步代码的可读性
ajax(1000)
.then(value=>{
ajax(2000)
})
.then(value=>{
ajax(1000)
})
.catch(err=>{
console.log(err);
})
//like sync mode
try{
const url = ajax("url.json")
const url1 = ajax("url1.json")
const url2 = ajax("url2.json")
}catch(e){
console.error(e);
}
如何像同步代码一样执行呢
function * foo(){
console.log("start");
const res = yield 'foo' //yield的返回值是下一次next执行传入的参数
console.log(res)
}
const geneator = foo() //不会立刻执行,生成器对象
const result = geneator.next() //开始执行生成器中的代码
console.log(result) //start {value:"foo",done:false}
const result1 = geneator.next("hello") //从上一次yield开始持续往下执行,next可传入参数,
console.log(result1); //hello {value:undefined,done:true}
//done示意执行器是否执行实现,yield相似于return然而不会完结函数执行,会暂停生成器,直到再次next()调用
生成器的throw办法
function * foo1(){
try{
const res = yield 'asdasdasdsa'
// console.log(res)
}catch(e){
console.log(e)
}
}
const ge = foo1()
//执行到第一个yield 返回对象.value就是此次yield执行后的返回值
console.log(ge.next("第一次传入")) //{ value: 'asdasdasdsa', done: false }
ge.throw(new Error("error1")) //抛出异样,s会被catch捕获并打印
应用生成器执行异步,like async mode
function * main(){
try{
const lastdata = yield ajaxT(1000)
// console.log(lastdata)
const lastdata1 = yield ajaxT(2000)
// console.log(lastdata)
}catch(e)
{
console.log(e)
}
}
const g = main()
const result11 = g.next();
(result11.value as Promise<any>).then(data=>{
const result22 = g.next(data);
if(result22.done) return
(result22.value as Promise<any>).then(data=>{
const result33 = g.next(data)
if(result33.done) return
})
})
封装生成器调用办法,递归
//递归封装一个生成器
function co(geneator){
const g = geneator()
function handleResult(result){
if(result.done) return
result.value.then(data=>{
const nextresult = g.next(data)
handleResult(nextresult)
},err=>{
g.throw(err)
})
}
handleResult(g.next())
}
co(main)
发表回复