深度剖析JavaScript - 2(异步编程)

JavaScript采用单线程模式工作的原因:
最早JavaScript语言是运行在浏览器的脚本语言,目的是实现页面上的动态交互,实现页面交互的核心是dom操作,这也就决定了必须使用单线程模型,否则会出现很复杂的线程同步问题
JavaScript单线程指的是:
JS在执行环境中负责执行代码的线程只有一个
JavaScript单线程模式优点:
更安全,更简单
JavaScript单线程模式缺点:
如果碰到一个特别耗时的任务,后面任务都得排队等待此耗时任务的结束,导致整个程序的执行会被拖延,造成“假死”的情况
为了解决JavaScript耗时阻塞这种问题,JavaScript将任务的执行模式分成为两种:同步模式、异步模式

同步模式

代码当中的任务依次执行,后一个任务必须等待前一个任务结束才能执行, 大多数任务都会以同步模式执行

异步模式

如:ajax操作或者nodeJS中的大文件读写
不会去等待这个任务的结束才开始下一个任务,对于耗时任务都是开启过后立即往后执行下一个任务
这种模式解决了单线程的JavaScript无法同时处理大量耗时任务的问题,但是会造成代码执行的顺序混乱

回调函数(JavaScript实现异步编程方案的根基)

回调函数可以理解为一件很想做的事,但是并不知道它所依赖的任务什么时候能够完成,最好的办法是把做这件事的步骤写到函数中,交给任务的执行者,任务执行者会在任务结束帮你执行你想要做的事

如果直接使用传统的回调方式去完成复杂的异步流程,无法避免大量的回调函数嵌套,就会导致回调地狱的问题。为了解决回调地狱问题,使用promise规范

promise实际上就是一个对象,用来表述一个异步任务最终结束过后 究竟是成功还是失败 (就像是对外界做出的承诺,最开始这个承诺是待定状态pending,最终可能成功Fulfilled,也有可能失败rejected,无论是成功或是失败,都会有相应的反应onFulfilled 、onRejected

// Promise是 JS 2015提供的全局类型,可以用 Promise 去构造一个Promise实例
// Promise需要接收一个函数作为参数, 这个函数会在构造Promise过程中同步执行
const promise = new Promise( function(resolve, reject) {
   
	resolve("成功");  // 调用resolve是将这个Promise对象的状态修改为成功
	// reject(new Error('失败'));  
	// 调用 reject是将这个Promise对象的状态修改为失败 
	// (这里只能调用成功或失败 其中一个)
	// new Error()是一个全新的错误对象,描述为何失败
})

//  Promise的then方法去指定Promise中的resolve方法和reject方法
promise.then(function (value) {
   
	console.log(value); 
}, function (error) {
   
	console.log(error);
})

/*
这里如果Promise异步操作中哪怕没有异步操作,
then方法中定义的回调函数仍然需要进入回调队列中进行排队,
then方法中的回调函数必须等待线程中同步代码执行完成后才会执行
*/ 

Promise的链式调用
使用链式调用去避免回调的嵌套,尽量保证代码的扁平化
代码中的异常最好的形式是:明确捕获每一个可能的异常

  • Promise的then方法会返回一个全新的Promise对象
  • 后面的then方法就是为上一个then方法返回的Promise对象注册回调
  • 前面then方法中回调函数的返回值会作为后面then方法中回调函数的参数
  • 如果在回调函数中返回值是Promise对象时,那后面then方法中的回调函数就会等待这个Promise结束

Promise的catch方法注册失败回调函数,Promise对象中任何异常都会执行到catch中的失败回调函数
Promise链条上的异常会一直向后传递直至被捕获,使用catch方法注册失败回调更像是给整个链条注册失败回调,使用catch方法比使用then方法第二个失败回调更加通用

ajax('/api/users.json')
	.then(function (res) {
   
		console.log(1111);
	})  //  => Promise
		.then(function (res) {
   
		console.log(2222);
	})  //  => Promise
		.then(function (res) {
   
		console.log(3333);
	})  //  => Promise
	.catch((reject) => {
   
		console.log(reject);
	}
		 )

// 返回结果是 1111 =》 2222 =》 3333

Promise静态方法

  • Promise.resolve() 快速把一个值转化为Promise对象
  • Promise.reject()

Promise 并行执行
Promise.all() 入参为多个Promise对象组成的数组 是等待多有任务结束后才会结束
Promise.all() 允许按照异步API调用顺序得到异步API的执行结果

// all方法有个特点:所有promise对象全部返回成功状态时,Promise.all([]).then()进入成功回调,否则进入失败回调
function p1() {
   
	return new Promise((resolve, reject) => {
   
		setTimeout(() => resolve('p1'), 200);
	})
}
function p2() {
   
	return new Promise((resolve, reject) => {
   
		resolve('p2');
	})
}
Promise.all(['a', 'b', p1(), p2()]).then((result) => {
   
	console.log(result); // 输出结果顺序 ['a', 'b', p1, p2]
})

Promise.race() 只会等待第一个结束的任务,第一个任务结束,就结束了
这个Promise对象组结束后才会调用then方法和catch方法;

Promise执行时序 、宏任务、微任务
回调队列中的任务称为 宏任务;
宏任务执行过程中可以临时加上一些额外需求,这些额外需求可以选择作为一个新的宏任务进到队列中排队,也可以选择作为当前任务的微任务(微任务就是在当前宏任务结束过后立即执行的任务
Promise的回调就是作为微任务执行的

目前绝大多数异步调用都是作为宏任务执行,只有Promise对象、MutationObserver对象、node中的process.nextTick对象作为微任务在本轮宏任务执行结束后末尾执行

Promise方式的ajax

function ajax (url) {
   
	return new Promise(function (resolve, reject) {
   
		var xhr = new XMLHttpRequest(); 
		// 使用XMLHttpRequest对象去发送一个Ajax请求
		xhr.open('GET', url);
		// open 设置请求方式为get
		xhr.responseType = 'json'
		
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值