01 :了解异步
1:在js中事件,定时器,ajax,文件操作,数据库的操作,在js中耗时的操作,基本上都会设置成异步。
第一步:接下来我们以读取文件为例来了解promise的产生,首先新建一个dome
第二步在index.js写入读取文件的代码
import fs from 'fs'
fs.readFile('./text/a.txt','utf8',(err,data)=>{
console.log(data);
})
fs.readFile('./text/b.txt','utf8',(err,data)=>{
console.log(data);
})
fs.readFile('./text/c.txt','utf8',(err,data)=>{
console.log(data);
})
fs.readFile('./text/d.txt','utf8',(err,data)=>{
console.log(data);
})
在运行结果中发现异步的读取文件,是没有顺序的,每一次的运行结果都不一样,但是我想要的结果就是按顺序来读取a,b,c,d这四个文件,那应该怎么做呢。
答案:其实很简单,因为异步的操作,我们的值是在外面拿不到,但是我们可以在里面等第一个异步读取a文件结束后,拿到结果了,我在进行读取b文件....这样就解决了。
import fs from 'fs'
fs.readFile('./text/a.txt','utf8',(err,data)=>{
console.log(data);
fs.readFile('./text/b.txt','utf8',(err,data)=>{
console.log(data);
fs.readFile('./text/c.txt','utf8',(err,data)=>{
console.log(data);
fs.readFile('./text/d.txt','utf8',(err,data)=>{
console.log(data);
})
})
})
})
二:回调地狱的产生
- 通过上面这种嵌套的方式,虽然实现了异步读取文件的操作,但是又产生了一个问题,通过层层嵌套的方式,产生的回调函数嵌套很多层,一旦项目复杂度高,这时候就很难维护代码,我们把这种层层嵌套的方式称为回调地狱。
三:Premise
Premise的出现,就是为了解决回调地狱,解决异步的处理,他是ES6中新增的,Promise可以理解为一个容器,里面可以编写的异步的代码。
语法
l
- 一般用于处理成功后的数据:
resolve - 一般用于处理失败后的失败 :
rejects .then方法接收两个函数类型的参数,分别用于接收resolve的值,rejects的值.then方法也可以接收一个参数,表示只接收resolve的值,失败的结果可以通过链式调用的catch方法捕获
let p=new Promise ((resolve,rejects)=>{
if(/* */){
resolve(value)
}else{
rejects(error)
}
})
promise的三个状态
pending(默认) fulfilled(成功) rejected(失败)
- resolve函数被执行时, 会将promise的状态从 pending 改成 fulfilled 成功
- reject函数被执行时, 会将promise的状态从pending 改成 rejected 失败
并发的任务Promise.all
一、 Promise.all([promise1, promise2, promise3]) 等待原则, 是在所有promise都完成后执行, 可以用于处理一些并发的任务
场景:常用于loading效果的显示
后面的.then中配置的函数, 是在前面的所有promise都完成后执行, 可以用于处理一些并发的任务
Promise.all([promise1, promise2, promise3]).then((values) => {
values 是一个数组, 会收集前面promise的结果 values[0] => promise1的成功的结果
})
二 、Promise.race([promise1, promise2, promise3]) 赛跑, 竞速原则, 只要三个promise中有一个满足条件, 就会执行.then(用的较少)
四 Premise实例用法
.then()可以接收两个参数,第一个函数处理成功,第二个函数失败
import fs from 'fs'
const p1= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/a.txt','utf8',(err,data)=>{
console.log(data);
if(err=null){
// 没有错误,就把读取到的信息,通过resolve放到fs.readFile()外面去,
resolve(data)
}else{
// 有错误,就把错误通过reject放到fs。readFile()外面去
reject(err)
}
})
})
// p1里面就存储这resolve和reject抛出的值
// then()可以接收两个参数,第一个函数处理成功,第二个函数失败
p1.then(res=>{
console.log(res);
},err=>{
console.log(err);
})
第二种方法.catch来读取
第二种方法用.catch方法来捕获错误
p1.then(res=>{
console.log(res,"文件读取成功");
}).catch(err=>{
console.log(err,"文件读取失败");
})
五:解决回调地狱
import fs from 'fs'
const p1= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/a.txt','utf8',(err,data)=>{
resolve(data)
})
})
const p2= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/b.txt','utf8',(err,data)=>{
resolve(data)
})
})
const p3= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/c.txt','utf8',(err,data)=>{
resolve(data)
})
})
const p4= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/d.txt','utf8',(err,data)=>{
resolve(data)
})
})
// p1里面就存储这resolve和reject抛出的值
// then()可以接收两个参数,第一个函数处理成功,第二个函数失败
p1.then(res=>{
console.log(res);
})
p2.then(res=>{
console.log(res);
})
p3.then(res=>{
console.log(res);
})
p4.then(res=>{
console.log(res)
})
console.log(p1,p2,p3,p4);
输出结果
从输出结果来看虽然Promise用resolve把成功的结果返回到外面来了,通过.then的方法接收这Promise返回结果,但是从输出结果来看还是没有解决异步的问题
当然我也可以这样做,.then嵌套,但是这样回调地狱又产生了,始终没有解决,那Promise的意义也就看不出来了。
p1.then(res=>{
console.log(res);
p2.then(res=>{
console.log(res);
p3.then(res=>{
console.log(res);
p4.then(res=>{
console.log(res);
})
})
})
})
六:Promise完整写法
1: then()中的回调函数,不写返回值,:默认返回一个空数组Promise对象 如果返回一个真实的Promise对象,就会赋值给then()
**不return结果的时候,返回就是一个空Promise对象**
import { log } from 'console'
import fs from 'fs'
const p1= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/a.txt','utf8',(err,data)=>{
resolve(data)
})
})
const p2= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/b.txt','utf8',(err,data)=>{
resolve(data)
})
})
const p3= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/c.txt','utf8',(err,data)=>{
resolve(data)
})
})
const p4= new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile('./text/d.txt','utf8',(err,data)=>{
resolve(data)
})
})
// p1里面就存储这resolve和reject抛出的值
// then()可以接收两个参数,第一个函数处理成功,第二个函数失败
const a= p1.then(res=>{
console.log(res);
return p2
})
const b= a.then(res=>{
console.log(res);
return p3
})
const c= b.then(res=>{
console.log(res);
return p4
})
c.then(res=>{
console.log(res);
})
// then()中的回调函数,不写返回值,:默认返回一个空数组Promise对象 如果返回一个真实的Promise对象,就会赋值给then()
- 当然上面.then的方式用链式编程的写法来写就方便多了
- 返回一个Promise对象,调用下一个.then()
p1.then(res=>{
console.log(res);
return p2
}).then(res=>{
console.log(res);
return p3
}).then(res=>{
console.log(res);
return p4
}).then(res=>{
console.log(res);
})
七 封装Promise
import fs from 'fs'
// promise(resolve,reject)的封装
function getPromise(url){
return new Promise((resolve,reject)=>{
// 在函数中,一定要有异步代码
fs.readFile(url,'utf8',(err,data)=>{
resolve(data)
})
})
}
//调用
getPromise('./text/a.txt').then(res=>{
console.log(res);
return getPromise('./text/b.txt')
}).then(res=>{
console.log(res);
return getPromise('./text/c.txt')
}).then(res=>{
console.log(res);
return getPromise('./text/d.txt')
}).then(res=>{
console.log(res);
})
八 第三方包then-fs 解决回调地狱。
// then.fs解决回调地狱
// 普通的fs模块,readFile()返回 undefined
// this-fs模块,readFile()返回,promise对象
import thenFs from 'then-fs';
thenFs.readFile('./text/a.txt','utf8').then(res=>{
console.log(res);
return thenFs.readFile('./text/b.txt','utf8')
}).then(res=>{
console.log(res);
return thenFs.readFile('./text/c.txt','utf-8')
}).then(res=>{
console.log(res);
return thenFs.readFile('./text/d.txt','utf8')
}).then(res=>{
console.log(res);
})
- 总结:Promise对象代表一个异步操作,有三种状态
- Pending(进行中),此时的Promise的结果为undefined
- Resolved(已完成,又称Fulfilled),此时Promise的结果为resolve函数的值
- Rejected(已失败),此时Promise的结果为传递给reject的函数的值 需要注意的是:Promise被创建的时候,执行的是同步代码 then()和catch()里面执行的异步代码
八 async和await修饰符
import fs from 'then-fs'
async function fn(){
const fs1=await fs.readFile('./text/a.txt','utf8')
console.log(fs1);
const fs2=await fs.readFile('./text/b.txt','utf8')
console.log(fs2);
const fs3=await fs.readFile('./text/c.txt','utf8')
console.log(fs3);
const fs4=await fs.readFile('./text/d.txt','utf8')
console.log(fs4);
}
fn()
ES7 标准中新增的 async 函数,从目前的内部实现来说其实就是 Generator 函数的语法糖。
它基于 Promise,并与所有现存的基于Promise 的 API 兼容。
async 关键字
async关键字用于声明⼀个异步函数(如async function asyncTask1() {...})async会⾃动将常规函数转换成 Promise,返回值也是⼀个 Promise 对象async函数内部可以使⽤await
await 关键字
await用于等待异步的功能执⾏完毕var result = await someAsyncCall()await放置在 Promise 调⽤之前,会强制async函数中其他代码等待,直到 Promise 完成并返回结果await只能与 Promise ⼀起使⽤await只能在async函数内部使⽤ 总结:await替代了then(),也不需要链式编程了
九. 相较于 Promise,async/await有何优势?
- 同步化代码的阅读体验(Promise 虽然摆脱了回调地狱,但 then 链式调⽤的阅读负担还是存在的)
- 和同步代码更一致的错误处理方式( async/await 可以⽤成熟的 try/catch 做处理,比 Promise 的错误捕获更简洁直观)
- 调试时的阅读性, 也相对更友好
try和catch捕获异常(扩展)
import fs from 'then-fs'
async function fn(){
try{
const fs1=await fs.readFile('./text/a.txt','utf8')
console.log(fs1);
const fs2=await fs.readFile('./text/b.txt','utf8')
console.log(fs2);
const fs3=await fs.readFile('./text/c.txt','utf8')
console.log(fs3);
const fs4=await fs.readFile('./text/ddd.txt','utf8')
console.log(fs4);
}catch{
console.log("文件读取错误");
}finally{
console.log("无论有没有错误都要执行的代码");
}
}
fn()
输出结果