文章目录
一、js中的 同步/异步 相关
1.1 基本概念
1、同步任务: 按顺序一个一个往下执行。
但同时,有些功能的代码执行时间比较长。比如:定时器 setTimeout() 函数,就可以设置一段延时执行的代码,那在这段空闲时间内,是不希望程序阻塞的,而是继续往下执行,这种情况下,如果还将 setTimeout() 设置为同步任务的话,显然就不合理了。---->
所以在 js 中除了同步任务外,还有异步任务。js 中常见的异步任务有:setTimeout() 定时器、Ajax
2、异步任务: 在执行的时候,不会跟同步任务一样按顺序走。而是当一个异步任务被创建之后,它会在一个特殊的队列中进行等待(任务队列),等待以后通过一定的机制,再添加到主线程中执行。
重点:异步任务的执行一定是在我们当前已经触发了这些同步任务之后去执行的。
1.2 案例说明
举个例子:
===代码一:
console.log('任务1:同步')
console.log('任务2:同步')
console.log('任务3:同步')
// function(){} 可以用箭头函数代替 ()=>{}
setTimeout(function (){
console.log('任务4:异步')
},1000)
============输出:
任务1:同步
任务2:同步
任务3:同步
任务4:异步
===代码二:
console.log('任务1:同步')
console.log('任务2:同步')
setTimeout(function (){
console.log('任务4:异步')
}) //不写定时器时间,默认为最小时间
console.log('任务3:同步')
============输出:
任务1:同步
任务2:同步
任务3:同步
任务4:异步
分析:
代码一:写了一个定时器,开始的时候是同步任务先触发(其中:任务1、2、3一定是按照顺序来执行的),然后再开始触发异步任务。
代码二:由 setTimeout 触发的异步任务,它内部的代码并不会在这个位置执行。因为异步任务会在当前存在的同步任务都执行完毕后再被触发。
注意: 跟定时器的时间没有关系,时间决定了:如果你存在多个异步任务,决定了异步任务之间的一个执行顺序:
二、Promise
2.1 上面案例带来的问题 ---- 回调地狱
1、思考:代码二中,这样的编写程序会有什么影响?----> 首先:在实际任务中,每个任务都可能是复杂的代码。其次:如果说任务3 需要 依赖于 任务4 处理后的结果,那怎么办?
setTimeout(function (){
console.log('任务4:异步')
console.log('任务3:同步')
})
那我直接把任务3 放到任务4 中,那也还行。假如在执行任务3 时,发现它后面还有异步任务,这时怎么办呢?----> 直接嵌套
setTimeout(function (){
console.log('任务4:异步')
console.log('任务3:同步')
setTimeout(function (){
console.log('任务5:异步')
console.log('任务6:异步')
})
})
直接嵌套的方式肯定不行 ----> 会导致代码出现很多的嵌套层数(也叫做函数的回调地狱)
方式:所以为了解决这个问题,需要用到 Promise。----> 能够 将这种复杂的嵌套式的异步结构 书写为 一种更接近于同步的写法
2.2 promise的基本使用
2、使用:
const p1 = new Promise((resolve, reject) => {
})
console.log(p1)
Promise 是个类,直接通过 new 创建 Promise 实例,其中参数是一个回调函数,并且回调函数有两个参数:
① resolve:异步任务执行成功的一个工具
② reject:异步任务执行失败的一个工具
通过先打印 p1:
-
每个 Promise 对象都具备 状态 这个概念,此时 pending 就是等待的状态,也就是说现在 promise 对象并没有被分配一个状态(等待是默认状态)
成功、失败、等待怎么在实际开发中理解?----> 任务执行肯定有成功或失败,比如说像服务端请求数据,拿到了一个正确数据,那就成功,否则就失败。也有可能请求发出去了,服务端没有给响应,这个过程中判定不了是成功还是失败,此时就处于等待的状态。
然后再去使用 resolve、reject 做个观察:其中调用 resolve()、reject(),就触发了 resolve、reject 这两个工具:
我们创建的 promise 实例(也就是 p1),它其实就标记了在 new promise 中设计的异步任务的一个处理结果,所以可以利用它进行后续处理。
Promise 提供了三个常用的方法:
方法 | 含义 | |
---|---|---|
then | 成功后 | 只要是成功的状态(fulfilled)就自动调用 |
catch | 失败后 | rejected 状态就自动调用 |
finally | 都会执行 | 无论失败与否,最后都会执行 |
const p1 = new Promise((resolve, reject) => {
// resolve("成功后的数据")
reject("失败后的数据")
})
p1.then(data => {
/* 这个data可自命名,含义:就是成功后,传递的结果
data怎么来?就是上面调用resovle中传递的一个信息
*/
console.log(data) // ---->输出:成功后的数据
}).catch(err => {
console.log(err) // ---->输出:失败后的数据
})
代码分析:new Promise 中的异步任务的结果,无论是成功还是失败,下面都会获取到,并且你上面执行,我下面就可以同步获取到结果。
2.2.1 .then()嵌套
成功的情况:假如我还有异步任务呢?也就是说我第一个异步任务触发之后,还有异步任务怎么办?----> 直接在 then 中 return Promise 对象即可:
const p1 = new Promise((resolve, reject) => {
resolve("任务1成功后的数据")
// reject("任务1失败后的数据")
})
p1.then(data => {
console.log(data)
return new Promise(((resolve, reject) => {
resolve("任务2成功后的数据")
// reject("任务2失败后的数据")
}))
})
//这个.then就是处理上面then中return的Promise对象
.then(data1 => {
console.log(data1)
})
.catch(err => {
console.log(err)
})
所以这就避免了嵌套的层数,即使后面还有异步任务,也只需要套一层。
失败的情况:但是假如第一个 promise 失败了,由于任务是连续的,后面都会失败。然而这个 catch 只是第一个 then 的 catch,我想不同 then 有不同的 catch 怎么办?----> 跟成功一样,也 .catch ? 发现不行。所以:
每个 then 其实支持两个参数:第一个是函数(成功时触发),第二个也是函数(失败时触发)。如果第二个函数没有,就走 catch。所以就可以不用 catch,直接写第二个参数
const p1 = new Promise((resolve, reject) => {
// resolve("任务1成功后的数据")
reject("任务1失败后的数据")
})
p1.then(data => {
console.log(data)
return new Promise(((resolve, reject) => {
// resolve("任务2成功后的数据")
reject("任务2失败后的数据")
}))
}, err => {
console.log("任务1失败了")
})
.then(data => {
console.log(data)
}, err => {
console.log("任务2失败了")
})
为什么第二个错误输出为 undefined,也就是第二个错误没有触发?----> 在设置失败函数参数的时候,应该设置返回值,如果没设置,代表当前 then 是没有返回值的,就会默认返回一个成功 promise 对象,导致后面的 then 触发成功的函数参数。----> 所以需要设置返回值,方法:
-
返回一个新的 promise,然后这个 promise 是一个失败的状态
-
推荐:直接抛出异常:
err => { console.log("任务1失败了") throw new Error('任务1失败了') }
2.2.2 fetch使用
这个 fetch 不是新内容,也是上面 promise 的一种用法,更多用于请求,其用法一样:
fetch('url').then(...)
.then(...)
至此,关于 promise 的简单介绍和使用完毕!后续第二篇内容开始在 promise 的基础上介绍 async、await
附上链接:点击查看