文章摘要
本片文章包含以下几个要点:
- JS(JavaScript )语言的执行机制?
- 什么是同步任务,什么是异步任务?
- 传统的回调函数嵌套,造成回调地狱如何解决?
- Promise的基本使用和原理?
- Promise 的代码执行机制?
- Async 和 await 的使用。
1 : JavaScript 执行机制
JavaScript 是一门单线程脚本语言 , 由于Dom 的产生页面无法执行多线程,JavaScript 语言 由上到下有序进行执行
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。 为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
2: 什么是同步任务,什么是异步任务?
异步任务
异步任务:当JS发现异步任务存在时时,JavaScript 会提交给对应的异步进行处理程序(提交给浏览器)进行处理,当异步任务处理完成的的时候,JS会将该任务加入到异步任务队列中,进行排队,此时JS主线程会一直查询异步消息队列中是否有任务,如果有则 拿到主线程(执行栈)中执行。
常见的异步任务 :
-
settimeout(()=>{}.1000) setinterval(()=>{},1000) 定时器
-
执行的事件 onclick onchange onload 等…
-
ajax中的onload
-
.then (异步回调)
-
process.nextTick(callback[, …args])
同步任务 :
同步任务: 赋值操作,for 循环 if 分支 运算,等,JavaScript 基础代码执行的为同步任务, JS同步任务会加入到主线程(执行栈)中进行排队执行。
什么是回调地狱 ?
名词解释 :在回调函数中继续嵌套回调函数,造成结构模糊
功能需求 : 实现 三个文件依次读取文件内容
实现思路 : 当第一个文件读取成功时进行第二个文件读取,以此类推
产品经理 吐槽: 结构不清晰,不简单明了,垃圾代码,不会封装函数吗??
// -------异步嵌套----------------------------------------
fs.readFile("./files/1.txt",'utf-8',(err,dataStr)=>{
if(err) return console.log(err.message);
console.log(dataStr);
fs.readFile("./files/2.txt",'utf-8',(err,dataStr)=>{
if(err) return console.log(err.message);
console.log(dataStr);
fs.readFile("./files/3.txt",'utf-8',(err,dataStr)=>{
if(err) return console.log(err.message);
console.log(dataStr);
})
})
})//** 什么 xxx 玩意 你来 **
使用 Promise 实现
回调变少了但是 .then 好像变多了
import fs from 'fs'
// 主函数
const ReadFile = (path) => {
return new Promise((resolve, reject) => {
// 成功的 调用 resolve
// 失败的调用 reject
fs.readFile(path, 'utf-8', (err, data) => {
if (err) return reject(err)
resolve(data)
})
})
}
ReadFile("../File/1.txt").then(res => {
console.log(res);
// 当走到.then 中 代表执行成功 继续调用读取第二个文件
return ReadFile("../File/2.txt")
}).then(res => {
console.log(res);
return ReadFile("../File/3.txt")
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err.message);
})
接下来就是
Promise的基本使用和原理
先介绍 Promise 的三种状态 !
pending (进行中) ,promise对象
没有被resolve
或者reject
时状态是 pending
const p1 = new Promise((resolve, reject) => { }) // 此状态为 pending
Fulfilled (成功状态)
const p1 = new Promise((resolve, reject) => {
resolve("成功")
})
p1.then(res => {
console.log(res);
})
rejected(失败状态)
const p1 = new Promise((resolve, reject) => {
reject("失败")
})
p1.then(res => {
console.log(res);
})
new Promise 干了什么事?
PS: Promise 是一个构造函数,它代表着一个异步操作,可在里面拿到异步回调的结果,判断成功还是失败.
1: Promise对象接收一个实参 (函数 ) ,当Promise new 的时候 这个实参函数 会被马上被立即调用
2:new Promise 返回的是一个 Promise实例,该实例对象原型身上有一个 then()方法
3:该方法接收两个函数 (第一个回调是成功,第二个回调为失败)
这个成功 的函数会赋值给 resolve
这个失败 的函数会赋值给 reject
当 Promise内部 resolve 时.then 即可拿到成功时的结果 见上图 成功回调
当 Promise内部 reject 时.擦tch 即可拿到失败时的结果 见上图 失败回调
4:Prmise 原型身上还有一个 finally函数 不论是成功还是失败都会执行
Promise 状态不可变
Promise 的 三种状态,状态只能从 pending —> Fulfilled 或 pending ----> rejected
当 promise 状态一旦确认了,就无法进行更改了(那怎么确认它的状态呢? 也就是在 resolve 或 reject 时就确认了)
由此可发现当我第二次 reject时候 无法再进行更改了(所以这就是Promise 的状态不可更改)
总结 : Promise 执行流程
1: Promise()实例对象接收一个 function 函数 ,当 Promise 对象被new 实例是 function 函数会被立即调用 , 此时 function 函数体内的同步代码会被立即执行,Promise 实例对象的原型(Prototype)上有一个.then 方法 该方法接收两个函数 ((成功回调)=>{},(失败回调)=>) 此时内部会将该函数赋值给 resolve 和reject 当异步代码执行成功时 会调用resolve() 方法 并将 执行成功的代码传给 成功回调 如果代码执行失败 会将失败的 错误信息 err 传给 失败回调,成功时可通过 .then获取结果 失败可通过.catch 获取结果
Promise 身上的静态方法
Promise.all 方法
1.1 .all 方法 传递一个数组作为参数,当所有的Promise 被成功解析时返回的是成功结果的数组,当其中有任何有个Promise 出现了=异常, 则会返回此实例回调失败(reject),失败原因的是第一个失败 promise 的结果,并且在错误之前 之后的promise 结果也不会返回
function requestFirst () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第一个请求返回');
}, 1000)
})
}
function requestSecond () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第二个请求返回');
}, 1000)
})
}
function requestThird () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第三个请求返回');
}, 1000)
})
}
let requestArr = [requestFirst(), requestSecond(), requestThird()]
Promise.all(requestArr).then(res => {
console.log('结果1', res);
}).catch(err => {
console.log('错误1', err);
})
三个都成功 时 返回成功的结果
当有一个出现失败 则返回失败结果
如何解决?
1 : 调用 allSettled 方法,该方法不论是正常还是异常都会执行then 方法。 2: 用 try catch 捕获异常当前的异常信息 将其返回
解决方案 1 :
allSettled方法 ----> 不论是错误还是正确都一并返回结果
方案二 : 遍历数据 根据try catch 进行捕获
function one () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第一个请求返回');
}, 1000)
})
}
function two () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第二个请求返回');
}, 1000)
})
}
function three () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第三个请求返回');
}, 1000)
})
}
let arr = [one(), two(), three()]
async function handleAllFunc (fun) {
try { // 如果结果有错 则把错误捕获并返回 否则返回成功结果
const res = await fun
return res
} catch (error) {
return error
}
}
Promise.all(arr.map(item => handleAllFunc(item))).then(res => {
console.log('成功', res);
}).catch(err => {
console.log('失败', err);
})
reac方法
race()方法返回一个Promise,当可迭代对象中的一个Promise成功或拒绝时,该Promise 就会根据结果返回带有该Promise的值或错误的原因。
实际上 就是 多个Promise 对象作为参数传递到race 方法中 谁先执行完成先返回谁的值,如果出现错误则返回错误的原因
传递一个Promise返回结果的 数组 // 例: 上方的Promise结果 数组 const arr = [one(), two(), three()]
Promise.race([数组]).then(res => {
console.log(res);
})
allSettled方法
该方法 一起返回 成功和错误的Promise结果
function one () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第一个请求返回');
}, 1000)
})
}
function two () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('这个是错误的');
}, 1000)
})
}
function three () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('第三个请求返回');
}, 1000)
})
}
let requestArr = [one(), two(), three()]
Promise.allSettled(requestArr).then(res => {
//返回失败和成功的结果
console.log(res);
})
基本使用就到这里结束了 Async 和 await 放在下篇文中进行讲解,谢谢,欢迎大家评论留言。