发布时间:2023-08-04 10:00
说promise之前必须先简单说下,回调地狱
回调地狱:在回调函数中又嵌套了多层回调函数,便会形成回调地狱
JS中或node中,都大量的使用了回调函数进行异步操作,而异步操作什么时候返回结果是不可控的,如果我们希望几个异步请求按照顺序来执行,那么就需要将这些异步操作嵌套起来,嵌套的层数特别多,就会形成回调地狱 或者叫做 横向金字塔。
例如我们创建一个文件夹,里面各自包含一句诗,要求这四个异步按顺序排列
如此大量使用回调函数,便会形成回调地狱
//第一步先导入fs模块
import { readFile } from 'fs'
readFile('./poetry/a.txt', 'utf-8', (err, data) => {
console.log(data)
readFile('./poetry/b.txt', 'utf-8', (err, data) => {
console.log(data)
readFile('./poetry/c.txt', 'utf-8', (err, data) => {
console.log(data)
readFile('./poetry/d.txt', 'utf-8', (err, data) => {
console.log(data)
})
})
})
})
为了解决回调地狱带来,代码难于维护和修改的弊端,ES6推出Promise对象来解决回调地狱
Promise对象是一个构造函数,用来生成Promise实例。是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。
类似一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
从语法上说,Promise 是一个对象,使用的时候需要 new
而Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
实例中,它里面的异步操作就相当于一个承诺,而承诺就会有两种结果,要么完成了承诺的内容,要么失败。
Promise有 resolve(完成) 和 reject(失败) 两个形参
必须传入一个函数作为Promise的参数,这个函数在 new Promise的时候就会执行
将异步任务成功的结果传给 resolve 函数;将失败的信息传给 reject 函数
//第一步先导入fs模块
import { readFile } from 'fs'
//第二步创建promise对象
const p1 = new Promise((resolve, reject) => {
readFile('./poetry/a.txt', 'utf-8', (err, data) => {
resolve(data) //暂不做判断,不考虑错误情况
})
})
const p2 = new Promise((resolve, reject) => {
readFile('./poetry/b.txt', 'utf-8', (err, data) => {
resolve(data) //暂不做判断,不考虑错误情况
})
})
const p3 = new Promise((resolve, reject) => {
readFile('./poetry/c.txt', 'utf-8', (err, data) => {
resolve(data) //暂不做判断,不考虑错误情况
})
})
const p4 = new Promise((resolve, reject) => {
readFile('./poetry/d.txt', 'utf-8', (err, data) => {
resolve(data) //暂不做判断,不考虑错误情况
})
})
//第三步读取文件
const a = p1.then(res => {
console.log(res); //3.1 then()中的回调函数,不写返回值,默认返回一个空白的Promise对象,
//3.2如果返回一个真实的Promise对象,那么就会赋值给 then()
return p2 // 3.3返回一个 Promise 对象,调用下一个 then();
})
const b = a.then(res => {
console.log(res);
return p3
})
const c = b.then(res => {
console.log(res);
return p4
})
const d = c.then(res => {
console.log(res)
})
Promise 实例具有then方法,then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
//第一步导入fs模块
import { readFile } from 'fs'
//第二步封装一个方法.返回一个promise对象
function getpromise(url) {
return new Promise((resolve, reject) => {
readFile(url, 'utf-8', (err, data) => {
resolve(data) //暂不做判断,不考虑错误情况
})
})
}
//调用
getpromise('./poetry/a.txt').then(res => {
console.log(res);
return getpromise('./poetry/b.txt')
}).then(res => {
console.log(res);
return getpromise('./poetry/c.txt')
}).then(res => {
console.log(res);
return getpromise('./poetry/d.txt')
}).then(res => console.log(res))
注意:then方法接收一个函数类型的参数,只处理成功
then方法接收两个函数类型的参数,分别用于接收 resolve 的值 和 reject 的值
then方法也可以只接收一个参数,表示只接收 resolve 的值,失败的结果可以通过链式调用catch方法捕获
//需要先下载模块: npm i then-fs
// // 普通fs模块,readFile()返回 undefined ;
// // then-fs模块,readFile()返回 Promise 对象;
//先导入then-fs模块
import thenFs from 'then-fs'
// 读取文件
thenFs.readFile('./poetry/a.txt', 'utf8').then(res => {
console.log(res);
return thenFs.readFile('./poetry/b.txt', 'utf8');
}).then(res => {
console.log(res);
return thenFs.readFile('./poetry/c.txt', 'utf8');
}).then(res => {
console.log(res);
return thenFs.readFile('./poetry/d.txt', 'utf8');
}).then(res => console.log(res))
Promise被创建的时候,执行的是同步代码;
new Promise 和 new 其他对象一样,是同步任务。
但是获取结果时(调用 resolve 触发 then方法时)是异步的。
// then()和catch()里面执行的是异步代码;
ES2017 标准引入了 async 和 await,使得异步操作变得更加方便。
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
async 用于修饰一个 function
async 修饰的函数,总是返回一个 Promise 对象
函数内的所有值,将自动包装在 resolved 的 promise 中
async function fn() {
return 666
}
let result = fn()
console.log(result) //Promise { 666 }
result.then(res => console.log(res)) //666
正常情况下await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
await只能出现在异步函数中!
await能停止代码执行,让后面的同步代码,先执行
await后面跟随的是一个promise对象
await返回的是: Promise对象中的then()中的回调函数中的参数res
async function fn() {
return 666
}
console.log('嘻嘻') //第一个同步先执行
async function fn2() {
console.log('哈哈') //第二个同步
let str = await fn() // await会 暂停函数的执行 但不会影响同步
console.log(str)
console.log(888);
}
fn2()
console.log(333) //第三个同步
let result = fn()
//先导入then-fs模块
import thenFs from "then-fs";
async function getpromise() {
const a = await thenFs.readFile('./poetry/a.txt', 'utf8')
const b = await thenFs.readFile('./poetry/b.txt', 'utf8')
const c = await thenFs.readFile('./poetry/c.txt', 'utf8')
const d = await thenFs.readFile('./poetry/d.txt', 'utf8')
console.log(a)
console.log(b)
console.log(c)
console.log(d)
}
getpromise()
7.promise解决回调地狱(王者)
await需要等待promise执行完毕,所以 await会 暂停函数的执行,但不会影响其他同步任务。
await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中
7.1promise三种状态
Promise对象代表一个异步操作,
有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
一旦状态改变,就不会再变
状态的改变只有两种可能:
从pending(进行中)变为fulfilled(已成功)
从pending(进行中)变为rejected(已失败)
当达到最终的 fulfilled 或 rejected 时,promise的状态就不会再改变了。
7.2Promise.prototype.finally()
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
下面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行
// 先导入then-fs
import thenFs from 'then-fs';
async function fn() {
try {
const str1 = await thenFs.readFile('./poetry/a.txt', 'utf8');
console.log(str1);
const str2 = await thenFs.readFile('./poetry/b.txt', 'utf8');
console.log(str2);
const str3 = await thenFs.readFile('./poetry/c.txt', 'utf8');
console.log(str3);
const str4 = await thenFs.readFile('./poetry/d.txt', 'utf8');
console.log(str4);
} catch (e) {
console.log("文件读取错误: " + e.message);
} finally {
console.log('无论有没有错误都要执行的代码...');
}
}
fn();
未完待续…