抄作业

了解 Promise ?

Promise 是一种异步编程的解决方案;有三种状态,pending (进行中)、resolved(已完成)、rejected(已失败)。当Promise的状态由 pending 转变为 resolved 或 rejected 时,会执行相应的方法。状态一旦改变,就无法再次改变状态。
特点:Promise 的特点是只有异步操作的结果,可以决定当前是哪一种状态,其他操作都无法改变这个状态。

Promise 解决的痛点是什么 ?

  • 多次异步调用的结果,顺序可能不同步
  • 异步调用的结果如果存在依赖,则需要嵌套
    在ES5中,当进行多层嵌套回调时,会导致代码层次过多,很难进行维护和二次开发;而且会导致回调地狱的问题。
  1. 解决了 回调地狱 的问题;
  2. **promise 可以支持多个并发的请求,获取并发请求中的数据 **
  3. promise 可以解决可读性的问题
  4. promise 可以解决信任问题;对于回调过早、回调过晚或没有调用和回调次数天少或太多,由于promise只能决议一次,决议值只能有一个,决议之后无法改变,任何then中的回调也只会被调用一次,所以这就保证了Promise可以解决信任问题

Promise 解决的痛点还有其他方法可以解决吗? 如果有,请举例

**Promise 解决的通电还有其他方法可以解决,比如 setTimeout,事件监听,回调函数,Generator函数,async/await **

  • setTimeout:缺点不精确,只能确保在一定时间后加入到任务队列,并不保证立马执行。只有执行引擎栈中的代码执行完毕,主线程才会取读取任务队列
  • 事件监听: 任务的执行不取决于代码的顺序,而取决于某个事件是否发生
  • Generator函数:虽然将异步操作表示的很简洁,但是流程管理却不方便(即何时执行第一阶段,何时执行第二阶段).

Promise 的问题? 解决办法?

问题

  1. promise 一旦执行,无法中途取消
  2. promise 的错误无法在外部被捕捉到,只能在内部进行预判处理
  3. promise 的内部如何执行,监测起来很难
    解决办法
    使用ES7引入的 async,await 来处理异步

Promise 语法

new Promise(function(resolve, reject){
//...
})
Promise 的构造函数接收一个参数,是函数,并且传入两个参数 resolve 和 reject,分别表示异步操作执行成功后的回调函数 和 异步操作执行失败后的回调函数。

Promise 的基本使用

// 封装
function returnAsync() {
	var promise = new Promise((resolve, reject) => {
		// 异步操作
		setTimeout(() => {
			console.log("执行完成")
			let num = Math.ceil(Math.random()*10)
			if( num <= 5 ) {
				resolve(num); // 成功
			} else {
				reject("数字太大") // 失败
			}
		}, 1000)
	})
	return promise
}
// 使用方式一,在then添加两个参数,第一个表示成功回调,第二个表示失败回调
returnAsync()
.then((data) => {
	console.log("成功回调", data)
	console.log(newnum); // 此处的newnum未定义
	// 使用第二个参数作为异常回调,会报错卡死JS
}, (error) => {
	console.log("失败回调", error)
})

// 使用方式二,catch的用法 (当then中有代码抛出异常时,并不会报错卡死js,而是会进到catch方法中,相当于我们的tyr/catch语句功能)
returnAsync()
.then((data) => {
	console.log("成功回调", data)
	console.log(newnum); // 此处的newnum未定义
})
.catch((error) => {
	console.log("失败回调", error)
})
.finally(() => {
	console.log("无论成功失败都会执行")
})

Promise 多次处理异步请求 (链式调用)

实际开发场景一:
同时请求多个接口,比如:在请求完 接口1 之后,需要根据接口1的数据继续请求接口2,以此类推...
实际开发场景二:
一个大的区域需要做一个Loading,后端分别给了好几个接口,此时需要全部请求完成之后才移除Loading

// 封装发送请求函数,返回一个Promise
function ajaxQuery(url) {
	let promise = new Promise((resolve, reject) => {
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function () {
			if(xhr.readyState != 4) return;
			if(xhr.readyState == 4 && xhr.status == 200) {
				// 处理正常情况
				resolve(xhr.responseText);
				// xhr.responseText 是从接口拿到的数据
			} else {
				// 处理异常情况
				reject("接口请求失败")
			}
		}
		xhr.responseType = 'json'; // 设置返回的数据类型
		xhr.open('get', url)
		xhr.send(null); // 请求接口
	})
	return promise;
}

// 使用: 发送多个ajax请求并且保持顺序
ajaxQuery("http://localhost:3000/api1")
.then((data1) => {
	// 请求完接口1后,继续请求接口2
	return ajaxQuery("http://localhost:3000/api2")
})
.then((data2) => {
	// 请求完接口2后,继续请求接口3
	return ajaxQuery("http://localhost:3000/api3")
})
.then((data3) => {
	// 获取接口3返回的数据
	console.log("接口3的数据", data3)
})
.catch((error) => {
	console.log("异常:", error)
})

Promise回调函数的返回值

两种情况:
情况一: 返回Promise实例对象,返回的该实例对象会调用下一个 then

Promise.resolve().then(() => 'Hello World!')
.then((value) => {
	console.log("fulfilled:", value); // Hello World!
})
.catch((value) => {
	console.log("rejected:", value)
})

情况二: 返回普通值,返回的普通值会直接传递给下一个 then

Promise.resolve().then(() => {
	return Promise.resolve("Hello")
})
.then((value) => {
	console.log("fulfilled:", value) // Hello
})
.catch((value) => {
	console.log('rejected:', value)
})

Promise 常用API

Promise.resolve(value)

# 类方法,该方法返回一个以 value 值解析后的 Promise 对象
# 普通值
Promise.resolve([1, 2, 3])
.then((value) => {
	console.log(value) // [1,2,3]
})

# 另一个promise
let otherPromise = Promise.resolve(33)
Promise.resolve(otherPromise)
.then((value) => {
	console.log(value) // 33
})

Promise.reject()

# 类方法,且与resolve唯一的不同是,返回的promise对象的状态为rejected
function asyncPromise() {
	try {
		console.log(newNum); // 未定义,会走catch
		return Promise.resolve(100);
	} catch (error) {
		return Promise.reject(error)
	}
}
// 使用
asyncPromise()
.then((value) => {
	// newNum未定,则走catch,如果去掉这返回 100
	console.log(value)
})
.catch((error) => {
	// error ReferenceError: newNum is not defined
	console.log(error)
})

Promise.prototype.then
实例方法,为Promise注册回调函数,fn().then((value) => {}),value 是上一个任务的返回结果,then中的函数一定要 return 一个结果 或者一个新的 Promise 对象,才可以让之后的 then回调接收
Promise.prototype.catch
实例方法,捕获异常,fn().catch((error) => {}), error 是 catch 注册之前的回调抛出的异常信息
Promise.race()

# 类方法,多个Promise 任务同时执行,返回最先执行结束的 Promise 任务结果,不管这个 Promise 结果是成功还是失败  
let p1 = new Promise((resolve, reject) => {
	setTimeout(resolve, 500, 'one')
})
let p2 = new Promise((resolve, reject) => {
	setTimeout(resolve, 100, 'two')
})
Promise.race([p1, p2]).then((value) => {
	console.log(value) // 两个都完成 two 最快
})
# 注意:使用场景可以用于 提示网络不佳等

Promise.all()

# 类方法,多个Promise任务同事执行。如果全部完成执行,则以数组的方式返回所有Promise任务的执行结果。只要任何一个输出的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息    

let p1 = Promise.resolve(10)
let p2 = 1773
let new Promise((resolve, reject) => {
	setTimeout(resolve, 100, 'foo')
})
Promise.all([p1, p2, p3])
.then((values) => {
	console.log(values); // [10, 1773, "foo"]
})

问题:为什么 promise 需要引入微任务?

Promise 中的执行函数是同步进行的,但是里面存在着异步操作,在异步操作结束后会调用 resolve 方法, 或者中途遇到错误调用reject 方法,这两者都是作为微任务进入到 EventLoop 中。但是你有没有想过, Promise 为什么要引入微任务的方式来进行回调操作?
若 promise 回调放在进行宏任务队列的尾部,若队列非常长,那么回调迟迟得不到执行,造成的效果就是应用卡顿。所以就引入了微任务
将回调函数放到当前宏任务中的最后面。
这样,利用微任务解决了两大痛点:

  1. 采用异步回调替代同步回调解决了浪费 CPU 性能的问题
  2. 放在当前宏任务最后执行,解决了回调执行的实时性问题
原文地址:https://www.cnblogs.com/yuxi2018/p/14469580.html