异步编程方式:回调函数、Promsie、async、await
Js 微任务和宏任务
- js是单线程的语言
- js代码执行流程:同步执行完 ==》 事件循环。同步的任务都执行完了,才会执行事件循环的内容,进入事件循环:请求、定时器、事件。。。
- 事件循环中包含:【微任务、宏任务】
微任务;promise.then (then 里面的代码才是 微任务)
宏任务:setTimout。。
要执行宏任务的流程 先要执行完事件循环中的 微任务
先执行完 再执行 事件循环中的 “宏任务” = “微任务”
要执行宏任务的前提是清空了所有的微任务
流程:同步==》事件循环「微任务和宏任务」 == 〉 微任务执行完再值执行宏任务
setTimout(function(){
console.log('宏任务1') // 宏任务
})
new Promise((resolve) =>{
console.log('1 promise 1') // 同步任务
resolve()
}).then(() =>{
console.log('微任务1')
}).then(() =>{
console.log('微任务2')
})
console.log('同步2') // 同步任务
打印顺序 1 promise 1' => 同步2 => 微任务1 => 微任务2 =>宏任务1
// 每隔一秒,打印一个5
for (var i =0;i<5;i++){
//setTimout为
setTimeout(function(){
console.log(i)
},1000)
}
/*
任务队列
setTimeout(function(){
console.log(5)
},1000)
setTimeout(function(){
console.log(5)
},1000)
setTimeout(function(){
console.log(5)
},1000)
setTimeout(function(){
console.log(5)
},1000)
setTimeout(function(){
console.log(5)
},1000)
*/
每隔几秒输出几?
// 先打印3,再隔一秒打印3,再隔2秒打印3
for (var i =0;i < 3 ;i++){
setTimout(function(){
console.log(i)
},1000 * i)
}
/*
先执行主线程的代码,再执行事件队列的任务。
setTimout(function(){
console.log(3)
},1000 * 0)
setTimout(function(){
console.log(3)
},1000 * 1)
setTimout(function(){
console.log(3)
},1000 * 2)
*/
Promsie
在Promise出现之前,如果需要进行异步操作,一般都使用回调函数。但是如果回调函数嵌套层数过多,就会造成回调地狱:
Promise的内部原理是什么?它的优缺点是什么
Promise对象,封装了一个异步操作并且还可以获取成功或失败的结果
Promise主要就是解决回调地狱的问题,之前如果异步任务比较多,同时他们之间有相互依赖的关系,就只能使用回调函数处理,这样就容易形成回调地狱,代码的可读性差,可维护性也很差
有三种状态; pending初始状态 fulfilled 成功状态 rejected失败状态
状态改变只会有两种情况:pending -> fulfilled,pending ->rejected 一旦发生,状态就会凝固,不会再变
首先我们无法取消promise ,一旦创建它就会立即执行,不能中途取消,如果不设置回调,promise内部抛出的错误就无法反馈到外面,若当前处于pending状态时,无法得知目前在那个阶段。
原理:构造一个Promise实例,实例需要传递函数的参数,这个函数有两个形参,分别是函数类型,一个是resolve一个是reject。
Promise上还有then方法。这个方法就是来指定状态时的确定操作,resolve是执行第一个函数,reject是执行第二个函数。
Promise的基本用法
创建promise对象
Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
then方法
当Promise执行的内容符合成功条件时,调用resolve函数,失败就调用reject函数。那么这里的resolve函数在哪定义的呢?其实你传给then的函数就是resolve函数。
let MyPromise = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
resolve(user)
},1000)
})
MyPromise.then(function (param){
console.log(param)
})
catch方法
当Promise对象除了有then方法,还有一个catch方法。比如,当我们在Promise里面抛出异常,调用了reject方法,这个reject方法是在哪定义的呢?其实你传给catch的函数就是这个reject方法。
let MyPromise = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
reject("当前出现了网络错误,返回404")
},1000)
})
MyPromise.then(function (param){
console.log(param)
}).catch(function (param){
console.log("调用了catch方法")
console.log(param)
})
all方法
假设我们需要有3个异步任务(比如3个异步请求),我们希望在3个异步任务全部执行完毕之后,一起把请求结果打印出来,应该如何实现呢?答案是使用all方法。
all方法可以完成并行任务,它接收一个数组,数组的每一项都是一个promise对象。当数组中所有的promise的状态都达到resolve的时候,all方法的状态就会变成resolveed,如果有一个状态变成了rejected,那么all方法的状态就会变成rejected。
let promsie1 = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
resolve(1)
},1000)
})
let promsie2 = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
resolve(2)
},1000)
})
let promsie3 = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
resolve(3)
},1000)
})
// 有一个返回 reject,就会执行catch
Promsie.all([promise1,promise2,promise3]).then((res) =>{
console.log(res)//结果为:【1,2,3】
}).catch((err) =>{
console.log("执行了catch")
console.log(err)
})
race方法
假设有这么一个场景,我们发送了3个请求,我们只需要返回速度最快的请求。比如2号请求最先返回,那么我们直接打印2号的返回结果,不管1号和3号了,那么这种情况下就需要使用race
race方法和all 一样,接受的参数是一个每项都是promsie的数组,但是与all不同的是,当最先执行完的事件执行完之后,就直接返回该promise对象的值。如果第一个promise对象状态变成resolved,那么自身的状态变成了resolved;反之第一个promise变成rejected,那自身状态就会变成rejected
let promsie1 = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
resolve(1)
},1000)
})
let promsie2 = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
resolve(2)
},2000)
})
let promsie3 = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
resolve(3)
},3000)
})
// 有一个返回 reject,就会执行catch
Promsie.race([promise1,promise2,promise3]).then((res) =>{
console.log(res)//结果为:1
}).catch((err) =>{
console.log("执行了catch")
console.log(err)
})
race 和 all 的区别 1.Promise.all 接受一个数组作为参数,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。 需要注意,Promise.all获得的成功结果的数组里面的顺序和Promise.all接收到的数组顺序是一致的,这样当遇到发送多个请求并根据顺序获取和使用数据的场景,就可以使用Promise.all来解决。 2.Promise.race Promise.race就是赛跑的的意思,换而言之,Promise.race([p1,p2,p3])里面哪个结果获得快,就返回那个结果,不管结果本身是成功状态还是失败状态。当要做一件事超过多长时间就不做了,就可以用这个方法来解决。
finally方法
finally方法用于指定不管Promsie对象最后状态如何,都会执行的操作
let MyPromise = new Promise(function (resolve,reject){
setTimeout(function(){
const user = {id:1,name:"wangjianguo"}
reject("当前出现了网络错误,返回404")
},1000)
})
MyPromise.then(function (param){
console.log(param)
}).catch(function (param){
console.log("调用了catch方法")
console.log(param)
}).finally(() =>{
console.log("执行了finally方法")
})
async、await
async 用于声明一个异步函数,await用于等待一个异步方法执行完成。当然语法上强制规定await只能出现在async函数中,先来看看async函数返回了什么。
async function test(){
//相当于 Promise.resolve("hello world")
return "hello world"
}
let result = test()
console.log(result)
result.then((v) =>{
console.log(v) // hello world
})
async、await的优点
- 使用Promise,try/catch难以处理在JSON.parse过程中的问题,原因是这个错误发生在Promsie内部。
const getJSON = () =>{
const jsonString = "{invalid JSON data}"
return new Promsie((resolve,reject) =>{
setTimeout(() =>{
reject("网络请求发生错误")
},1000)
})
}
const makeRequest = () => {
try {
getJSON().then((result) =>{
const data = JSON.parse(result)
console.log(data)
}).catch (err) {
console.log("第一个catch")
console.log(err)
}
} catch (err) {
console.log("第二个catch")
console.log(err)
}
}
const makeRequest2 = () => {
try {
// async、await 在try catch 中更容易捕获错误
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log("第二个catch")
console.log(err)
}
}
makeRequest()
- 在条件判断中减少嵌套层数
// Promise 用法
const makeRequest = () => {
retrun getJSON().then((data) =>{
if (data.needdsAnotherRequest) {
return makeAnotherRequest(data).then((moreData) =>{
console.log(moreData)
return moreData
})
}else {
console.log(data)
return data
}
})
}
// async、await用法
const makeRequest = () => {
const data = await getJSON()
if (data.needdsAnotherRequest) {
const moreData = await makeAnotherRequest()
console.log(moreData)
return moreData
}else {
console.log(data)
return data
}
}
promise 和 async await的区别
- 都是处理请求的方式,
- promise是ES6的语法,async await 是ES7的语法
- async await 是基于promise实现的,他和promise都是非阻塞性的
优缺点:
-
promise 是返回对象我们要用then,catch方法去处理和捕获异常,并且书写方式是链式,容易造成代码重叠,不好维护,async await 是通过try catch 进行捕获异常
-
async await 最大的优点就是能让代码看起来像同步一样,只要遇到await就会立刻返回结果,然后再执行后面的操作
promise.then()的方式返回,就执行了后面的操作。