【浅谈】JavaScript异步操作和Promise

JavaScript异步操作和Promise

博主最近由于需要在学习社区进行一次技术分享,所以选择的题目就是JavaScript异步操作和promise,现在将资料整理为博客

1、JavaScript特性

首先JavaScript是单线程的吧,所以它本身并不可能是异步的。但是JS的承载环建(如浏览器、Node)是多线程的,这让JS具备了一定的异步属性

2、JavaScript异步API

在JS的异步操作中,我们应该熟识有哪些是异步操作的API,如:

  • setTimeout
  • readFile(Node环建下)
  • writeFile(Node环建下)
  • 最常用的Ajax
  • 。。。。

3、异步操作分析

现在,有一段代码是这样的,我们可以一起来分析分析如下代码

function add(x,y){
	console.log(1)
	setTimeout(function(){
		console.log(2)
		var ret = x + y
		return ret
	},1000)
	console.log(3)
}

console.log(add(1,2))

这段代码很简单,程序首先会进入add方法,然后输出1,之后遇到异步操作setTimeout时并不会等着setTimeout执行完,而是接着输出3,到此,函数体由于没有返回值,则会再输出一个undefined ,然后经过1S后,程序输出2
如下:
结果
可以看到,我们并不能拿到异步操作中的结果 ret ,所以现在我们有一个需求就是拿到异步操作中的结果,
这里,我写了两个大家可能会想到的方法

  1. 第一种方法
    在这里插入图片描述
    但是这种方法是行不通的,程序的输出依旧是undefined
  2. 第二种方法
    可能会想通过一个全局变量来接收ret
    在这里插入图片描述
    这样,程序确实能得到异步操作的结果
    在这里插入图片描述
    但是这种方法肯定是无意义的

4、回调函数

在这种情况下,我们可以通过一个回调函数来完成这一需求
即,在定义函数时,多加一个函数(通常将函数名命名为callback)作为其参数,如下

function add(x,y,callback){
	console.log(1)
	setTimeout(function(){
		var ret = x + y
		callback(ret)
	},1000)
}

add(10,20,function(data){
	console.log(data)
})

这样,就会将异步操作中的 ret 作为回调函数的参数去执行,从而从外部得到异步操作的结果
在这里插入图片描述

5、封装原生Ajax方法

相信大家都用过jquery封装的ajax方法发送异步请求,
如 $.get(‘xxxx’,function(){ }) 。大家就会发现往往异步操作都会伴有一个回调函数
所以我们从来没见过这样的操作吧 var ret = $.get()
现在为了更好的了解底层,我们可以自己尝试着封装原生的JS的ajax
首先,原生的JS的ajax
在这里插入图片描述
a.txt中的文件内容
在这里插入图片描述
在控制台中查看结果
在这里插入图片描述
现在我们通过callback进行封装自己的ajax方法

<script type="text/javascript">
	function get(url,callback){
		var oReq = new XMLHttpRequest()

		//当请求加载成功后调用此函数
		oReq.onload = function(){
			callback(oReq.responseText)
		}
		oReq.open('get',url,true)
		oReq.send()
	}

	//调用
	get('./a.txt',function(data){
		console.log(data)
	})
</scrpit>

6、Promise

6.1 应用场景

如果有多个异步操作,是无法保证其输出顺序的,如

//index.js
let fs = require('fs')

fs.readFile('./a.txt',function(err,data){
	if(err){
		console.log('读取文件失败')
	}
	console.log(data.toString()) //由于读取出来的文件是二进制文件,所以如果需要显示出来得使用 toString() 方法
})

fs.readFile('./b.txt',function(err,data){
	if(err){
		console.log('读取文件失败')
	}
	console.log(data.toString()) //由于读取出来的文件是二进制文件,所以如果需要显示出来得使用 toString() 方法
})

a和b的读取顺序是不定的
在这里插入图片描述
所以现在有一个需求就是让b在a之后执行
可能会想到通过嵌套的方式,如
在这里插入图片描述
这样的确可以保证输出顺序,但是如果代码一多,就不便于维护,进入了回调地狱
一个有意思的回调地狱的图片:
在这里插入图片描述

6.2 Promise完成回调嵌套

定义:Promise 首先是一个容器,里面存放了一个异步任务,异步任务的状态分为 resolved 和rejected
通过Promise优化代码
首先,创建两个promise对象,里面分别装读取a和b的文件操作

let fs = require('fs')

//new一个Promise对象
var p1 = new Promise(function(resolve,reject){
	fs.readFile('./a.txt',function(err,data){
		if(err){
			//将容器中的任务状态变为rejected
			reject(err)
		} else{
			resolve(data)
		}
	})
})

var p2 = new Promise(function(resolve,reject){
	fs.readFile('./b.txt',function(err,data){
		if(err){
			//将容器中的任务状态变为rejected
			reject(err)
		} else{
			resolve(data)
		}
	})
})

通过promise对象的 then 方法进行调用异步操作,then方法中传递两个函数参数
第一个函数参数是异步操作成功后,进行的操作,对应resolve(data),第二个则是失败时进行的操作(可以省略第二个参数),对应reject(err),如

p1.then(function(data){
	console.log(data.toString())
},function(err){
	console.log(err)
})

此时,程序输出
在这里插入图片描述
现在要实现嵌套操作,只需在第一个函数体中 return 另一个promise对象,然后接着调用 then 方法,如

p1
	.then(function(data){
		console.log(data.toString())
		return p2
	})
	.then(function(data){
		console.log(data.toString())
	})

此时就可以完成需求
在这里插入图片描述

6.3 Promise封装

可以将这个代码段进行封装,方便重复使用,如下

let fs = require('fs')

//封装
function pReadFile(path){
	return new Promise(function(resolve,reject){
		fs.readFile(path,'utf8',function(err,data){
			if(err){
				reject(err)
			} else{
				resolve(data)
			}
		})
	})
}

//调用
pReadFile('./a.txt')
	.then(function(data){
		console.log(data)
		return pReadFile('./b.txt')
	})
	.then(function(data){
		console.log(data)
	})

2019.4.3 23:30

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值