2021-07-23 回调地狱解决(Promise,异步函数基础语法)

本文详细介绍了如何使用Promise和异步函数解决JavaScript中的回调地狱问题。通过实例展示了Promise的构造和链式调用,以及异步函数的async/await语法,使代码更易于理解和维护。同时,还探讨了Node.js中util.promisify方法在转换异步API为Promise支持上的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2021/7/23
上次学习记录中,记录了异步API的回调函数,但往往我们在编程在回调函数中曾经出现一个现象,称之为“回调地狱”,我来一起来看看“回调地狱”是什么样子的

//需求: 能够依次读取文件1,2,3, 并依次拿到文件内容

const fs = require('fs');

fs.readFile('./1.txt', 'utf8', (err, result) => {
	console.log(result);
	fs.readFile('./2.txt', 'utf8', (err, result) => {
		console.log(result);
		fs.readFile('./3.txt', 'utf8', (err, result) => {
			console.log(result);
		})
	})
})

对于上述的代码,太过于累赘,并且不利用代码维护。而Promise函数可以帮我们解决回调地狱的问题


一、Promise

Promise出现的目标是解决Node.js异步编程中回调地狱的问题(它实际上是一个构造函数,所以在使用之前需要定对它进行实例化)

const fs = require('fs');

function p1 () {
	return new Promise((resolve, reject) => {
		fs.readFile('./1.txt', 'utf8', (err, result) => {
			/*resolve中的result中一个函数,调用了p1().then 中的回调函数,
			并把result传递给回调函数*/ 
			resolve(result);
		})
	})
}


function p2 () {
	return new Promise((resolve, reject) => {
		fs.readFile('./2.txt', 'utf8', (err, result) => {
			resolve(result);
		})
	})
}

function p3 () {
	return new Promise((resolve, reject) => {
		fs.readFile('./3.txt', 'utf8', (err, result) => {
			resolve(result);
		})
	})
}

//  then 中 return p2函数,对于链式编程中的后面的then来说就可以接收到p2的结果, p3同理
p1().then((r1) => {
	console.log(r1);
	return p2();
})
.then((r2) => {
	console.log(r2);
	return p3();
})
.then ((r3) => {
	console.log(r3);
})

总结

  1. 其实对于回调地狱就是多重嵌套的问题,对于Promise解决方式就是在原本的回调函数外面嵌套一个Promise构造函数,可以利用构造函数的resolve参数把回调函数的结果引到外面,对于代码看起来也就更加的简洁,并且方便维护。
  2. 但编程人员又要在合适的时间和位置来调用不同的Promise函数,所以我又用了一个函数来包裹它(如:p1(), p2(), p3();)
  3. p1().then((r1) => { console.log(r1); return p2(); }) 在这里return p2(), 对于后面的.then((r2) => { console.log(r2); return p3(); }) 中就可以拿到p2的返回结果,也加大了链式编程的方便性



二、Node.js异步函数

异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,全代码变得清晰明了。

const fn = async () = > {};
async function fn() {}
const fs = require('fs');

//1. 在普通函数定义的前面上加上async关键字,普通函数就变成了异步函数
//2. 异步函数默认的值是promise对象
//3. 在异步函数内部使用throw关键字进行错误的抛出
/*
	await关键字
	1.它只能出现在异步函数中
	2. await promise它可以暂停异步函数的执行,等待promise对象返回结果后再向下执行函数
*/

async function p1 (callback) {
	return 'p1';
}

async function p2 () {
	return 'p2';
}

async function p3 () {
	return 'p3';
}


//负责依次按顺序执行p1(), p2(), p3()
async function run() {
	let r1 = await p1();
	let r2 = await p2();
	let r3 = await p3();
	console.log(r1);
	console.log(r2);
	console.log(r3);
}

run();

结果如下:
请添加图片描述

异步函数总结

async关键字

  1. 普通函数定义前加上async关键字,普通函数变异步函数
  2. 异步函数默认返回promise对象,省略了构造函数实例化过程(new Promise())
  3. 在异步函数内部使用return 关键字进行结果返回,结果会被包裹在promise对象中,return关键字代替了resolve方法
  4. 在异步函数内部使用throw关键字抛出程序异常;
  5. 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
  6. 调用异步函数再链式调用then方法获取异步函数执行结果p1().then(function (data) { console.log(data); })

await关键字

  1. await关键字只能出现在异步函数中
  2. await promise 后面只能写promise对象,写其他类型的API是不可以的
  3. await关键字可以暂停异步函数向下执行,直到promise返回结果(和异步对象的then方法同效果,依次执行)

精进版(三个文件依次读取内容)

const fs = require('fs');
//promisify改造现在异步函数API,让其返回promise对象,从而支持异步函数语法
const promisify = require('util').promisify;
//调用promiseify方法改造现有异步API,让其返回promise对象
const readFile = promisify(fs.readFile);

async function run() {
	//使用经过改造的readFile方法可以返回promise对象,因此可以放在await后面
	let r1 = await readFile('./1.txt', 'utf8');
	let r2 = await readFile('./2.txt', 'utf8');
	let r3 = await readFile('./3.txt', 'utf8');
	console.log(r1);
	console.log(r2);
	console.log(r3);
}
run();

程序思路讲解:

  1. 问题1: 因为 fs.readFlie() 方法不能返回promise对象,那么就代表着不能使用异步函数调用
    回复1: 在util 模块中有一个promisify 方法,可以改造现有的异步API,让其返回 promise对象,从而支持异步函数语法(注: 在使用promisify方法之前要引用util 模块)
  2. 问题2: const promisify = require('util').promisify; 中,promisify中方法,为什么不是写成promisify()
    回复2: promisify是模块util中的一个方法,这个方法在代码中没有加“()”,代表此时此刻不是在调用该方法,而是获取promisify方法,并把它赋值给promisify,抽取这个promisify() 方便使用。(同理:const readFile = promisify(fs.readFile); 中也是获取到readFile方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值