JavaScript异步编程
JavaScript异步编程
采用单线程模式工作的原因
最早js语言就是运行在浏览器端的语言,目的是为了实现页面上的动态交互。实现页面交互的核心就是DOM操作,这就决定了它必须使用单线程模型,否则就会出现很复杂的线程同步问题。
假设在js中有多个线程一起工作,其中一个线程修改了这个DOM元素,同时另一个线程又删除了这个元素,此时浏览器就无法明确该以哪个工作线程为准。所以为了避免线程同步的问题,从一开始,js就设计成了单线程的工作模式。所以,js执行环境中负责执行代码的线程只有一个。
单线程的优势和弊端
这种模式最大的优势就是更安全,更简单,缺点也很明确,就是如果中间有一个特别耗时的任务,其他的任务就要等待很长的时间,出现假死的情况。
为了解决这种问题,js有两种任务的执行模式:同步模式(Synchronous)
和异步模式(Asynchronous)
。
同步模式与异步模式
同步模式API和异步模式API的特点:
-
同步模式的API的特点就是任务执行完代码才会继续往下走,例如:
console.log
-
异步模式的API的特点就是下达这个任务开启的指令之后代码就会继续执行,代码不会等待任务的结束
同步模式
同步模式 :指的是代码的任务依次执行,后一个任务必须等待前一个任务结束才能开始执行。程序的执行顺序和代码的编写顺序是完全一致的。在单线程模式下,大多数任务都会以同步模式执行。
console.log('hello')
function fn () {
console.log('fn')
}
function say () {
console.log('say hello')
fn()
}
say()
console.log('bye')
// hello
// say hello
// fn
// bye
// 使用调用栈的逻辑
异步模式
异步模式: 不会去等待这个任务的结束才开始下一个任务,都是开启过后就立即往后执行下一个任务。耗时函数的后续逻辑会通过回调函数的方式定义。在内部,耗时任务完成过后就会自动执行传入的回调函数。
console.log('timer1')
// 延时器
setTimeout(function timer1 () {
console.log('timer2')
}, 1800)
// 延时器中又嵌套了一个延时器
setTimeout(function timer2 () {
console.log('timer3')
setTimeout(function inner () {
console.log('timer4')
}, 1000)
}, 1000)
console.log('timer5')
// timer1
// timer5
// timer3
// timer2
// timer4
js线程某个时刻发起了一个异步调用,它紧接着继续执行其他的任务,此时异步线程会单独执行异步任务,执行过后会将回调放到消息队列中,js主线程执行完任务过后会依次执行消息队列中的任务。js是单线程的,浏览器不是单线程的,有一些API是有单独的线程去做的。
Promise——优雅的异步编程方案
Promise
是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。Promise
汉译过来就是承诺的意思,比传统的解决方案——回调函数和事件——更合理和更强大。Promise
简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
Promise的特点
-
它有三种状态:
pending
(进行中)、rejected
(已失败) -
它的状态是不可逆的,一旦状态改变,就不会再变。
-
即便
promise
中没有任何的异步操作,then
方法的回调函数仍然会进入到事件队列中排队。
Promise的基本用法
resolve
let promise=new Promise((resolve,reject)=>{
console.log('hello')
resovle('hi')
})
promise.then((data)=>{
console.log(data)
},(erro)=>{
console.log(erro)
})
//hello
//hi
reject
let promise=new Promise((resovle,reject)=>{
console.log('hello')
reject('erro')
})
promise.then((data)=>{
console.log(data)
},(erro)=>{
console.log(erro)
})
//hello
//erro
Promise案例
- 使用
Promise
封装一个ajax
请求:
function ajax(url){
return new Promise((resolve,reject)=>{
// 创建一个XMLHttpRequest对象去发送一个请求
const xhr=new XMLHttpRequest();
// 先设置一下xhr对象的请求方式是GET,请求的地址就是参数传递的url
xhr.open('GET',url)
// 设置返回的类型是json,是HTML5的新特性
// 我们在请求之后拿到的是json对象,而不是字符串
xhr.responseType='json';
xhr.onload=()=>{
console.log(xhr.status)
if(xhr.status==200){
//请求成功,返回结果
resolve(xhr.response)
}else{
//失败返回错误信息
reject(new Error(xhr.statusText))
}
};
//开始执行
xhr.send();
})
};
ajax('app.json').then((res)=>{
console.log(res)
},(err)=>{
console.log(err)
})
Promise链式调用
-
Promise
对象then方法,返回了全新的promise
对象。可以再继续调用then
方法,如果return的不是promise
对象,而是一个值,那么这个值会作为resolve
的值传递,如果没有值,默认是undefined
-
后面的then方法就是在为上一个
then
返回的Promise
注册回调 -
前面then方法中回调函数的返回值会作为后面
then
方法回调的参数 -
如果回调中返回的是
Promise
,那后面then方法的回调会等待它的结束