总所周知,
JavaScript是一门单线程执行的编程语言。也就是说,同一时间JavaScript只能做同一件事情。而一旦前一个任务非常耗时,那么后续的任务就不得不一致等待,从而造成程序的假死问题。
本章也就是从这个问题开始讨论,逐步认识 Promise
本篇的所有请求都是利用 json-server 这个包去模拟服务器
同步和异步
什么是同步
简而言之就是:代码自上而下逐行运行,完成了上一句才能下一句。
哪怕中间来个循环,也得等,这也就是同步的缺点。
什么是异步
通俗的来说,就是 主线程接着走,延迟和等待的程序挂载到另一个列表执行,不阻塞程序。
执行过程
1.同步任务由javascript主线程次序执行
2.异步任务委托宿主环境执行
3.已完成的异步任务对吸引的回调函数,会被加入到任务队列中等待执行
4.javascript主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
5.javascript主线程不断重复上面的第四步
异步的一个实例
function loadImag(src, resolve, reject) {
let image = new Image();
image.src = src;
image.onload = () => {
resolve(image);
} // 成功的回调函数
image.onerror = reject; // 失败的回调函数
}
loadImag(
"./logo.png",
image => {
document.body.appendChild(image);
console.log("图片加载成功")
},
() => {
console.log("图片加载失败")
}
);
console.log("啦啦啦");
异步的缺点
上面就是一个典型的异步问题,在代码中同样也会出现这样的问题。
//hd.js
function hd(){
console.log("hd.js");
}
//hc.js
function hc(){
hd();
console.log("hc.js");
}
// index.js
function load(src){
let script = document.createElement("script");
script.src = src;
document.body.appendChild(script);
}
load("./hd.js",()->{
hd();
});
load("./hc.js",()->{
hc();
});
不断刷新,时不时会报错
原因 :两个文件,谁先加载完,谁就先放在任务队列里面,因为hc.js依赖于hd.js,所以当hc.js先加载完了之后,就会报错,但是hd.js先加载完不会报错
回调地狱
上面暴露的问题,其实解决起来非常的简单。
function load(src){
let script = document.createElement("script");
script.src = src;
document.body.appendChild(script);
}
load("./hd.js",()->{
load("./hc.js",()->{
hc();
});
});
在这个时候,浏览器会先加载 hd ,再加载 hc ,放进任务队列的顺序就不会变,所以无论怎么刷新,都不会报错。
但是,当我们的模块依赖过多,且一层套着一层的时候,就会出现所谓的 回调地狱 问题。
如上面, 代码冗长,且不便于异常处理。
初识Promise
为什么要使用 Promise
这个问题其实在上文已经说到,回调地狱 的 代码冗长且不易于处理。
Promise的优点就格外的明显了:
- Promise 支持链式编程
- Promise 可以随时监听异步任务的状态,随时指定回调函数,一个或者多个
Promise实例
Promise 实现 ajax
首先我们不用 Promise 实现 ajax。
const btn = document.querySelector('#btn')
// 不用 Promise
btn.addEventListener('click',function(){
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET','http://127.0.0.1:3000/posts/1')
// 发送
xhr.send();
// 处理响应结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response)
}else{
console.log(xhr.status)
}
}
}
})
接下来,我们初步体验一下 Promise
const btn = document.querySelector('#btn')
// 用 Promise
btn.addEventListener('click', function () {
const p = new Promise((resolve, reject) => {
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET', 'http://127.0.0.1:3000/posts/1')
// 发送
xhr.send();
// 处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
// 调用 then 方法
p.then(value => {
console.log(value)
},
reason => {
console.log(reason)
}
)
})
可以看到,Promise 有两种状态,
成功和失败,并且把这两个状态分别抽象成了一个回调函数。
Promise 封装 ajax
function sendAJAX(method, url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.send()
// 处理结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
}
sendAJAX('GET', 'http://127.0.0.1:3000/posts/1').then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
Promise 对象中的属性
Promise对象的状态
Promise 实例对象中有一个属性 PromiseState,这个属性有三个状态:
pending 等待 / 未决定的状态
resolved / fullfilled 成功
rejected 失败
Promise 状态改变:
Promise 的状态改变,有且只有两种:
pending 变为 resolved
pending 变为 rejected
每一个 Promise 对象只能改变一次。无论是成功还是失败,都会有一个结果数据,成功的结果数据一般称为 value ,失败的结果数据一般称为 reason 。
Promise对象的值
Promise 对象中的另一个属性 PromiseResult 保存这对象 【成功/失败】 的结果。
Promise 的工作流程
Promise 的 API
Promise 中的 then 方法
一个 Promise 函数可以调用多个 成功/失败 的函数。并且,then如果没有处理发送出来的东西,将一直向下找,直到找到
let p = new Promise((resolve,reject)=>{
resolve('ok')
})
p.then()
p.then(value => {
console.log(value)
})
p.then(value => {
alert(value)
})
上面的例子其实也可以看出 then 方法返回的其实也是一个 Promise 对象,下面这个例子看的更加的清楚。
let p1 = new Promise((resolve, reject) => {
reject("rejected");
});
let p2 = p1
.then(
value => console.log(value),
reason => console.log(reason)
)
.then(
a => console.log("成功"),
b => console.log(b)
);
// 输出 rejected 成功
接下来。我们讨论 then 方法的返回结果。
let p3 = new Promise((resolve,reject) => {
resolve('ok')
})
let result = p.then(value => {
// 1.抛出错误
throw '出了问题';
})
console.log(result)
如果抛出了问题,那么 then 方法就会返回一个 rejected 的结果,返回的值就是抛出错误的值。
let p3 = new Promise((resolve,reject) => {
resolve('ok')
})
let result = p3.then(value => {
// 2.返回结果是 非Promise 对象
return 521;
})
console.log(result)
如果返回的是一个 非Promise 对象,比如数字,字符串等等,那么 then 方法返回的是一个 resolve 的结果,返回的值就是 return 的东西。
let p3 = new Promise((resolve,reject) => {
resolve('ok')
})
let result = p3.then(value => {
// 3.返回的是一个 Promise 对象
return new Promise((resolve,reject)=>{
reject('Error')
},reason =>{
console.warn(reason)
})
})
console.log(result)
如果返回的是一个 Promise 对象,那么 then 方法返回的结果和值都由 返回的Promise 决定。
Promise.resolve 方法
这里的 resolve 方法是指 Promise 对象(而非实例)上的方法。
let p1 = Promise.resolve(521);
console.log(p1);
如果传入的参数为 非Promise类型的对象(字符串,数字,undefined等等),则返回的结果过为成功 Promise 对象。
let p2 = Promise.resolve(new Promise((resolve,reject) => {
resolve('ok')
}))
console.log(p2)
let p3 = Promise.resolve(new Promise((resolve,reject) => {
resolve('Error')
}))
console.log(p3)
如果传入的参数为 Promise 对象,则参数的结果决定了 resolve 的结果。
Promise.reject 方法
Promise.reject 永远返回的都是失败的对象,且失败对象的值就是传入的值。
let p1 = Promise.reject(521);
console.log(p1)
let p2 = Promise.reject(new Promise((resolve,reject) => {
resolve('ok')
}))
console.log(p2)
let p3 = Promise.reject(new Promise((resolve,reject) => {
reject('Error')
}))
console.log(p3)
Promise.all 方法
Promise.all 方法,数组内全部都成功,最终的结果成功;数组内有一个不成功,最终结果都是不成功,且值为不成功的那个Promise实例的值。
let p1 = new Promise((resolve,reject)=>{
resolve('ok')
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.all([p1,p2,p3]);
console.log(result)
let p1 = new Promise((resolve,reject)=>{
resolve('ok')
})
let p2 = Promise.reject('Error')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.all([p1,p2,p3]);
console.log(result)
Promise.race 方法
Promise.race 方法,返回一个新的 Promise ,第一个完成的 Promise 的结果状态就是最终的结果状态。
let p1 = new Promise((resolve,reject)=>{
resolve('ok')
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.race([p1,p2,p3]);
console.log(result)
这里返回的就是第一个 Promise 对象的结果。
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},1000)
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.race([p1,p2,p3]);
console.log(result)
因为前面有一个定时器,所以此时返回的是第二个Promise的结果。
Promise 的链式编程
链式编程的一个实例
let p = new Promise((resolve,reject) => {
setTimeout(()=>{
resolve('ok');
},1000)
})
p.then(value => {
console.log(value)
return new Promise((resolve,reject) => {
resolve('success')
})
}).then(value => {
console.log(value)
}).then(value => {
console.log(value)
})
then 方法返回的是一个 Promise ,而 Promise 的状态是由 then 方法的返回值来决定的,由于 倒数第二个then方法 没有返回值,所以 该then方法 返回的其实是一个 状态为resolve 值为 undefined 的 Promise。这也就是为什么最后打印的是 undefined了。
异常穿透
在链式编程的时候,我们不需要反复的去写 reject 的回调函数,只要在最后加上 catch 这个函数,在链式编程的任何位置发生错误,都能被捕获到。
let p = new Promise((resolve,reject) => {
setTimeout(()=>{
reject('Error');
},1000)
})
p.then(value => {
console.log(111)
}).then(value => {
console.log(222)
}).then(value => {
console.log(333)
}).catch(reason => {
console.warn(reason)
})
中断Peomise链
想要中断 Promise 链,其实很简单,只需要在想要中断的位置,返回一个 pending 状态的 Peomise 。
let p = new Promise((resolve,reject) => {
setTimeout(()=>{
reject('Error');
},1000)
})
p.then(value => {
console.log(111)
// 中断 Promise 链
return new Promise(()=>{})
}).then(value => {
console.log(222)
}).then(value => {
console.log(333)
}).catch(reason => {
console.warn(reason)
})
finally
finally 这个函数放在最后面,表示前面的东西无论成功与否,函数内的代码都会执行。
new Promise((resolve, reject) => {
reject("rejected");
}).then(
value => {
return new Promise((resolve, reject) => {
reject("p2");
});
})
.then(value => {
console.log(value);
}).catch(error => {
console.log(error)
})
.finally(() => {
console.log("永远执行");
})
自定义封装 Promise
环境搭建
function Promise(executor){
// resolve 函数
function resolve(data){
}
// reject 函数
function reject(data){
}
// 同步调用 【执行器函数】(Promise 内部的代码是同步调用的,所以必须调用这个函数)
executor(resolve,reject)
}
// 添加 then 方法
Promise.prototype.then = function(onResolve,onReject){
}
这样一个初步的 Promise 环境就搭好了。
resolve 和 reject 函数的实现
首先,我们在之前打印的 Promise 对象中,我们能发现,Promise 对象有两个属性,也就是上文经常提到的 状态和属性 。
所以,在实现 resolve和reject 函数的时候,首先必须定义这两个属性,并且在对应的函数中修改 状态和值 这两个属性。
function Promise(executor){
// 添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
const _this = this
// resolve 函数
function resolve(data){
// 1.修改对象的状态(promiseState)
_this.PromiseState = 'fulfilled'
// 2.设置对象结果值(promiseResult)
_this.PromiseResult = data
}
// reject 函数
function reject(data){
_this.PromiseState = 'rejected'
_this.PromiseResult = data
}
// 同步调用 【执行器函数】
executor(resolve,reject)
}
注意:上面的
const _this = this是因为原本下面的 this 指向全局,也就是 window ,所以我们必须在前面改变this的指向。
throw 抛出异常改变状态
如果不对异常进行处理的话,那么程序一旦 throw 了一个错误出来,程序直接停止执行,根本不会执行后面的代码。
通过之前的学习,我们知道,Promise throw出一个错误后,Promise 的状态变为失败,值为抛出的异常。
try{
// 同步调用 【执行器函数】
executor(resolve,reject)
}catch(e){
// 调用 reject 将状态变为 reject
reject(e)
}
将 Promise 的状态变为只能修改一次
上面这个例子,在我们之前学习的过程中,Promise 应该根据前一个(成功)状态进行改变,但是,我们封装的 Promise 会执行两次,最终下面的 reject 会覆盖掉上面的 resoleve。
解决的方法很简单,在resoleve 和 reject函数中加一个判断即可。
function reject(data){
// 判断状态
if(_this.PromiseState !== 'pending') return
_this.PromiseState = 'rejected'
_this.PromiseResult = data
}
封装 then 方法执行回调
Promise.prototype.then = function(onResolve,onReject){
// 调用回调函数
if(this.PromiseState === 'fulfilled'){
onResolve(this.PromiseResult)
}
if(this.PromiseState === 'rejected'){
onReject(this.PromiseResult)
}
}
异步回调的执行
当我们正常执行上面这个例子的时候,我们会发现,在1s后会输出ok。
但是用我们自己封装的 Promise 则不会,原因是我们自己封装后,上面的例子是一个同步代码,resolve 函数在 1s 后执行,在此之前,then 方法就被执行了,所以没有输出结果。
解决办法:
function Promise(executor){
// 添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
this.callback = {}
const _this = this
// resolve 函数
function resolve(data){
if(_this.PromiseState !== 'pending') return
_this.PromiseState = 'fulfilled'
_this.PromiseResult = data
// 调用成功的回调
if(_this.callback.onResolve){
_this.callback.onResolve(data);
}
}
// reject 函数
function reject(data){
if(_this.PromiseState !== 'pending') return
_this.PromiseState = 'rejected'
_this.PromiseResult = data
// 调用失败的回调
if(_this.callback.onReject){
_this.callback.onReject(data);
}
}
}
// 添加 then 方法
Promise.prototype.then = function(onResolve,onReject){
if(this.PromiseState === 'fulfilled'){
onResolve(this.PromiseResult)
}
if(this.PromiseState === 'rejected'){
onReject(this.PromiseResult)
}
// 判断 pending 状态
if(this.PromiseState === 'pending'){
// 保存回调函数
this.callback = {
onResolve,
onReject
}
}
}
解释一下吧,先执行下面的 then 方法,此时 状态还是 pending ,所以将函数保存在 callback 里面。然后执行异步里面的代码(这里是setTimeout里面的),执行 resolve 函数,调用成功的回调。
指定多个回调的实现
上面这个案例,根据之前所学习的知识,我们能得到,上面这个案例会在控制台和弹窗都打印 ok。
但是,我们封装的 Promise 只会弹出弹框。原因是:我们上面保存的回调函数被执行了两遍,前面的一次保存(控制台输出)被后面的一次保存(弹框输出)给覆盖了。因此只有一个输出。
解决办法:
function Promise(executor){
// 添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
this.callback = []
const _this = this
// resolve 函数
function resolve(data){
if(_this.PromiseState !== 'pending') return
_this.PromiseState = 'fulfilled'
_this.PromiseResult = data
// 调用成功的回调
_this.callback.forEach(item => {
item.onResolve(data)
})
}
// reject 函数
function reject(data){
if(_this.PromiseState !== 'pending') return
_this.PromiseState = 'rejected'
_this.PromiseResult = data
// 调用失败的回调
_this.callback.forEach(item => {
item.onReject(data)
})
}
}
// 添加 then 方法
Promise.prototype.then = function(onResolve,onReject){
if(this.PromiseState === 'fulfilled'){
onResolve(this.PromiseResult)
}
if(this.PromiseState === 'rejected'){
onReject(this.PromiseResult)
}
// 判断 pending 状态
if(this.PromiseState === 'pending'){
// 保存回调函数
this.callback.push({
onResolve,
onReject
})
}
}
同步修改状态 then 方法返回结果
上面这个例子,按之前所学,打印的应该是一个 状态为成功,值为undefined 的Promise。(Promise返回结果的确定)
但是,我们封装的直接打印 undefined。原因是:我们的 then 方法中没有返回任何值。
Promise.prototype.then = function (onResolve, onReject) {
return new Promise((resolve, reject) => {
// 调用回调函数
if (this.PromiseState === 'fulfilled') {
try {
// 获取回调的执行结果
let result = onResolve(this.PromiseResult)
if (result instanceof Promise) {
// 如果返回的是 Promise
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
// 如果返回的是 非Promise 数据
resolve(result)
}
}catch(e){
reject(e)
}
}
if (this.PromiseState === 'rejected') {
onReject(this.PromiseResult)
}
// 判断 pending 状态
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callback.push({
onResolve,
onReject
})
}
})
}
异步修改状态 then 方法的返回结果
这里一直是 pending 的原因是:
因为有一个异步,状态没改变,走了下面这段代码,返回了一个 pending 的Promise。
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callback.push({
onResolve,
onReject
})
}
解决方法:
if (this.PromiseState === "pending") {
// 保存回调函数
this.callback.push({
onResolved: function () {
try {
// 执行成功的回调函数
let result = onResolved(_this.PromiseResult);
// 判断
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch(e) {
reject(e)
}
},
onRejected: function () {
try {
// 执行成功的回调函数
let result = onRejected(_this.PromiseResult);
// 判断
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
});
}
解释一下,这里的原理和上面异步实例的原理差不多,先执行 then ,将 callback 保存,之后调用异步里面的那个 resolve (这里是)函数,走成功的回调,获取一个 Promise 的值,在根据状态和是否是Promise,并调用相应的函数。
封装和完善 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
return new Promise((resolve, reject) => {
// 封装函数
function callback(type) {
try {
// 获取回调的执行结果
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
// 如果返回的是 Promise
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
// 如果返回的是 非Promise 数据
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "fulfilled") {
callback(onResolved);
}
if (this.PromiseState === "rejected") {
callback(onRejected);
}
if (this.PromiseState === "pending") {
this.callback.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected)
},
});
}
});
};
这里就是将相似的代码抽象成一个函数。
此外,我们在正常的情况下执行下面的代码:
但是我们自己封装的 Promise 函数,却打印的是:
解决方法:
在 resolve 和 reject 函数中加上一个定时器。
setTimeout(() => {
_this.callback.forEach((item) => {
item.onResolved(data);
});
});
在 then 方法的回调函数也加上一个定时器。
if (this.PromiseState === "fulfilled") {
setTimeout(() => {
callback(onResolved);
});
}
if (this.PromiseState === "rejected") {
setTimeout(() => {
callback(onRejected);
});
}
catch 方法
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected)
}
异常穿透
错误的原因是,因为有异步,我们先执行 then 方法,保存 callback 中的两个函数,但是,我们传递的只有一个 onResolved 函数,onRejected 函数就为 undefined,因此报错。
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
if(typeof onRejected !== 'function'){
onRejected = reason => {
throw reason
}
}
if(typeof onResolved !== 'function'){
onResolved = value => {
return value
}
}
return new Promise((resolve, reject) => {
...其他代码
});
};
如果没有传,那么我们手动的添加一个这样的函数。这样,就完成了异常穿透功能。
resolve 方法
const p1 = Promise.resolve('ok')
console.log(p1)
实现:
Promise.resolve = function(value){
// 返回Promise对象
return new Promise((resolve,reject) => {
if(value instanceof Promise){
value.then(v => {
resolve(v)
},r => {
reject(r)
})
}else{
resolve(value)
}
})
}
reject 方法
const p2 = Promise.reject('Error')
console.log(p2)
实现:
Promise.reject = function(reason){
return new Promise((resolve,reject) => {
reject(reason)
})
}
all 方法
const p3 = Promise.resolve('111')
const p4 = Promise.reject('222')
const p5 = Promise.resolve('333')
const res = Promise.all([p3,p4,p5])
console.log(res)
实现
Promise.all = function(promises){
return new Promise((resolve,reject) => {
let count = 0
let arr = []
for(let i = 0;i<promises.length;i++){
promises[i].then(v => {
// 得知对象的状态是成功的
// 每个Promise对象都成功
count++
// 将当前 Promise 对象成功的结果,存入到数组中
arr[i] = v
if(count === promises.length){
resolve(arr)
}
},r => {
reject(r)
})
}
})
}
race 方法
const p3 = Promise.resolve('111')
const p4 = Promise.reject('222')
const p5 = Promise.resolve('333')
const race = Promise.race([p3,p4,p5])
console.log(race)
实现:
Promise.race = function(promises){
return new Promise((resolve,reject) => {
for(let i = 0;i<promises.length;i++){
promises[i].then(v => {
resolve(v)
},r => {
reject(r)
})
}
})
}
完整代码
function Promise(executor) {
// 添加属性
this.PromiseState = "pending";
this.PromiseResult = null;
this.callback = [];
const _this = this;
// resolve 函数
function resolve(data) {
// 判断状态
if (_this.PromiseState !== "pending") return;
// 1.修改对象的状态(promiseState)
_this.PromiseState = "fulfilled";
// 2.设置对象结果值(promiseResult)
_this.PromiseResult = data;
// 调用成功的回调
setTimeout(() => {
_this.callback.forEach((item) => {
item.onResolved(data);
});
});
}
// reject 函数
function reject(data) {
if (_this.PromiseState !== "pending") return;
_this.PromiseState = "rejected";
_this.PromiseResult = data;
// 调用失败的回调
setTimeout(() => {
_this.callback.forEach((item) => {
item.onRejected(data);
});
});
}
try {
// 同步调用 【执行器函数】
executor(resolve, reject);
} catch (e) {
// 调用 reject 将状态变为 reject
reject(e);
}
}
// 添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
if (typeof onRejected !== "function") {
onRejected = (reason) => {
throw reason;
};
}
if (typeof onResolved !== "function") {
onResolved = (value) => {
return value;
};
}
return new Promise((resolve, reject) => {
// 封装函数
function callback(type) {
try {
// 获取回调的执行结果
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
// 如果返回的是 Promise
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
// 如果返回的是 非Promise 数据
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "fulfilled") {
setTimeout(() => {
callback(onResolved);
});
}
if (this.PromiseState === "rejected") {
setTimeout(() => {
callback(onRejected);
});
}
if (this.PromiseState === "pending") {
this.callback.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
},
});
}
});
};
// 添加 catch 方法
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
// 添加 resolve 方法
Promise.resolve = function (value) {
// 返回Promise对象
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(value);
}
});
};
// 添加 reject 方法
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
};
// 添加 all 方法
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let count = 0;
let arr = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
// 得知对象的状态是成功的
// 每个Promise对象都成功
count++;
// 将当前 Promise 对象成功的结果,存入到数组中
arr[i] = v;
if (count === promises.length) {
resolve(arr);
}
},
(r) => {
reject(r);
}
);
}
});
};
// 添加 race 方法
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
}
});
};
async 和 await 语法糖
async 函数
async 其实就相当于 new Promise 的语法变式。
async function main(){}
let result = main()
console.log(result)
可以看到,当一个函数加上了 async 之后,调用这个函数的实例就变成了一个 状态为成功,值为undefined 的 Promise
async function main(){
/**
* 第一种情况
* 如果返回值是一个 非Promise 类型的数据
* 那么后面打印的结果是 状态为resolve 值为返回的521 的 Promise
*/
return 521
/**
* 第二种情况
* 如果返回的死一个 Promise 对象
* 那么后面打印的结果就是 状态和值为返回Promise的状态和值 的Promise
* 比如本例中,打印的就是 状态为reject 值为Error 的Prmise
*/
// return new Promise((resolve,reject) => {
// reject('Error')
// })
/**
* 第三种情况
* 如果抛出了一个异常
* 那么打印出来的就是 状态为失败 值为抛出的值 oh no 的Promise
*/
// throw 'oh no'
}
let result = main()
console.log(result)
可以看到,async 函数的返回结果和前面 then 方法的返回结果可以说是一模一样。
说的更明白一点:下面两个例子完全一致。
async function hd(){
return "521";
}
hd.then(value => console.log(value));
new Promise(resolve=>{
resolve("521");
}).then(value => console.log(value));
await 表达式
await 右侧的表达式一般为 Promise 对象,但是也可以是其他的值。
如果表达式是 Promise 对象,await 返回时 Promise 成功的值。
如果表达式时其他值,直接将此作为 await 的返回值。
async function main() {
let p = new Promise((resolve, reject) => {
resolve('ok')
})
let q = new Promise((resolve, reject) => {
reject('Error')
})
// 1.右侧为 Promise 的情况
let res1 = await p
console.log(res1)
// 2.右侧为其他类型的数据
let res2 = await 20
console.log(res2)
// 3.如果 Promise 是失败的状态
try {
let res3 = await p
}catch(err){
console.log(err)
}
}
main()
注意:
- await 必须写在 async 函数中,但 async 函数中可以没有 await。
- 如果 await 的 Promise 失败了,就会抛出异常,需要通过 try...catch 捕获处理。
用 async 和 await 结合读取 fs 模块
const fs = require("fs");
const util = require("util");
const minReadFile = util.promisify(fs.readFile);
async function main() {
try {
// 读取第一个文件的内容
let data1 = await minReadFile("./fs/1.txt");
let data2 = await minReadFile("./fs/2.txt");
let data3 = await minReadFile("./fs/3.txt");
console.log(data1 + data2 + data3);
} catch (err) {
console.log(err);
}
}
main();
用 async 和 await 结合发送 ajax 请求
function sendAJAX(method, url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.send()
// 处理结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
}
let btn = document.querySelector('#btn')
btn.addEventListener('click',async function(){
let posts = await sendAJAX('GET','http:/127.0.0.1:3000/posts/1')
console.log(posts)
})
class 与 await 相结合
function sendAJAX(method, url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.send()
// 处理结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
}
class Posts {
constructor(id){
this.id = id;
}
// 如果类中有 then 方法,那么 await 会查找类中的then方法
then(resolve,reject){
let post = sendAJAX('GET',`http://localhost:3000/posts/${this.id}`)
resolve(post)
}
}
async function get() {
let post = await new Posts('1');
console.log(post)
}
get()
如果我的类中有一个 then 方法,那么该类返回的是一个 Promise ,像上面的例子中,如果没有
resolve(post)这个语句,那么上面都打印不出来;又如果改为resolve(),那么下面打印的是 undefined。
当然异步也可以封装在类的内部。
class Posts {
async get(id){
let post = await sendAJAX('GET',`http://127.0.0.1:3000/posts/${id}`)
console.log(post)
return post
}
}
new Posts().get(1).then(value => {
console.log(value)
},reason => {
console.log(resaon)
})
并行执行技巧
环境搭建
function p1() {
return new Promise(resolve => {
setTimeout(() => {
resolve("zs");
}, 2000);
});
}
function p2() {
return new Promise(resolve => {
setTimeout(() => {
resolve("ls");
}, 2000);
});
}
async function hd() {
let h1 = await p1();
let h2 = await p2();
}
向上面这样,会先执行p1,后执行p2。
async function hd(){
let h1 = p1();
let h2 = p2();
let h1value = await h1;
let h2value = await h2;
}
这样执行,p1和p2peye.com是异步,同时执行。
async function hd(){
let res = await Promise.all([p1(),p2()]);
console.loh(res);
}
当然,也可以更简单一点,直接调用 Promise.all 这个api。
参考文献及视频
结语
好啦,本次分享就到这里。
文章如果有不正确的地方,欢迎指正,共同学习,共同进步。
若有侵权,请联系作者删除。