1 异步事件的处理
那什么时候我们会来处理异步事件呢?
一种很常见的场景应该就是网络请求了。
我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。
所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。
//1.
// request.js
function requestDate(url){
// 模拟网络请求
setTimeout(() =>{
// 拿到请求的结果
// url传入的是coderwhy, 请求成功
if(url == "coderwhy"){
//成功
let name = ["abc", "cba", "nba"];
}else{
let errMessage = "请求失败, url错误";
}
})
}
// main.js
requestDate("kobe")
//怎么让main.js这个文件得到网络请求name或者errMessage的结果?
//2.
function requestDate(url){
// 模拟网络请求
setTimeout(() =>{
// 拿到请求的结果
// url传入的是coderwhy, 请求成功
if(url == "coderwhy"){
//成功
let name = ["abc", "cba", "nba"];
return name
}else{
let errMessage = "请求失败, url错误";
return errMessage
}
})
}
// main.js
let result = requestDate("kobe")
//这样子时拿不到返回结果的,因为网络请求时异步的,拿到的至于requestDate的返回结果
//return name时setTimeOut的rutrun结果,是拿不到的
//3.
//解决方法:传入对应的回调函数
// request.js
function requestData(url, successCallback, failtureCallback) {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
// url传入的是coderwhy, 请求成功
if (url === "coderwhy") {
// 成功
let names = ["abc", "cba", "nba"]
successCallback(names)
} else { // 否则请求失败
// 失败
let errMessage = "请求失败, url错误"
failtureCallback(errMessage)
}
}, 3000);
}
// main.js
requestData("kobe", (res) => {
console.log(res);
console.log("发送请求成功")
.....很长很长代码
}, (err) => {
console.log(err)
})
// 更规范/更好的方案 Promise承诺(规范好了所有的代码编写逻辑)
function requestData2() {
return "承诺"
}
const chengnuo = requestData2()
* 这种回调的方式有很多的弊端:
* 1> 如果是我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称, 并且使用好
如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。
但是,当网络请求非常复杂时,就会出现回调地狱。
2 网络请求的回调地狱
151.PNG回调地狱,举个简单的例子,就是让你1s之后打印一个1,再2s之后打印一个2, 再3s之后打印一个3
setTimeout( () => {
console.log(1);
setTimeout( () => {
console.log(2);
setTimeout( () => {
console.log(3);
},3000)
}, 2000)
}, 1000)
如果这里不仅仅是打印,而是很长的逻辑的话,就会嵌套,很容易出问题。
3 Promise的介绍
117.PNGProxy是es6里面新增的api
function foo(){
return "承诺"
}
const chengnuo = foo()
//当我们调用foo的时候,foo不能马上给我们一个结果的时候,
//我们希望它给我们返回一个"承诺"
//在这里这个"承诺"就是Promise
//相当于下面代码
function foo(){
return new Promise()
}
const promise = foo()
//相当于
const promise = new Promise()
function Person(name, age){
}
const p = new Person("why", 18)
//像是p实例一样,promise里面也可以传入参数,其中参数是一个回调函数
//并且这个回调函数一传进去就会立刻执行
//传入的这个函数被称为Executor
const promise = new Promise(() => {
console.log("传入的函数被执行了");
})
/*
像是下面一样
class Promise{
constructor(callback){
callback()
}
}
*/
//而且executor执行的时候会给你再传入一些其他的参数
class PromiseTest{
constructor(callback){
let foo = function(){
};
let bar = function(){
};
callback(foo, bar)
}
}
const promiseTest = new PromiseTest(() => {
console.log("传入的函数被执行了");
})
//实际promise里面,这里的foo和bar是resolve和reject
const promise = new Promise((resolve, reject) => {
console.log("传入的函数被执行了");
})
//上面例子中的foo函数
function foo() {
// Promise
return new Promise((resolve, reject) => {
resolve("success message")
// reject("failture message")
})
}
const fooPromise = foo()
//返回promise之后,就是别人拿到promise之后,我们要做一些网络请求的东西
//成功之后给我成功的结果,失败给我失败的结果
//如果成功的话,给我调别人给我的resolve函数
//失败的话,给我调别人给我的reject函数
/*
在这里fooPromise只是一份承诺,不是一个结果
需要调用fooPromise的then方法
fooPromise.then(),并且 可以往里面再传入一个回调函数
在里面调用resolve方法的时候,外面的fooPromise.then()方法就会被自动执行
我们就知道这次做了一个成功的回调
里面调用reject的话,外面会自动调用fooPromise.catch()
*/
// promise.then(() => {
// })
// promise.catch(() => {
// })
4. 异步请求的Promise
//request.js
function requestData(url) {
// 异步请求的代码会被放入到executor中
return new Promise((resolve, reject) => {
// 模拟网络请求
setTimeout(() => {
// 拿到请求的结果
// url传入的是coderwhy, 请求成功
if (url === "coderwhy"){
//成功
let names = ["abc", "cba", "nba"];
resolve(names)
//这个时候回调函数就判断数据拿到了没有,所以说promise返回的是一个承诺,处理成功或者失败的承诺,后续的操作不再这里搞
//,接下来的很长很长处理数据的代码就放到then里面
//以前的处理方式是数据拿到的话,就执行回调函数,马上执行一大段代码,现在是判断和代码分开了,因为promise给了我们承诺
} else {
let errMessage = "请求失败, url错误";
reject(errMessage);
}
}, 3000)
} )
}
// main.js
const promise = requestData("coderwhy");
/* promise.then((res) => {
console.log("请求成功:", res)
})
promise.catch((err) => {
console.log("请求失败:", err)
}) */
/*
then和catch分开写会在node中报异常,
还有一直写法是在then里面直接传两个回调函数
一个是成功时候的回调,一个是失败时候的回调
then方法传入的回调函数两个回调函数:
第一个回调函数, 会在Promise执行resolve函数时, 被回调
第二个回调函数, 会在Promise执行reject函数时, 被回调
*/
promise.then((res) => {
console.log("请求成功:", res)
}, (err) => {
console.log("请求失败:", err)
})
5. Promise的三种状态
118.PNGpending
adj. 悬而未决的; 待定; 待决; 即将发生的;
一旦调用resolve(),就是fulfilled状态,调用reject,就是rejected状态
// const promise = new Promise((resolve, reject) => {
// })
// promise.then(res => {
// }, err => {
// })
// 完全等价于下面的代码
// 注意: Promise状态一旦确定下来, 那么就是不可更改的(锁定)
new Promise((resolve, reject) => {
// pending状态: 待定/悬而未决的
console.log("--------")
reject() // 处于rejected状态(已拒绝状态)
resolve() // 处于fulfilled状态(已敲定/兑现状态)
console.log("++++++++++++")
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
})
4 Promise的resolve参数
* resolve(参数)有三种情况
-
普通的值或者对象 pending -> fulfilled*
-
传入一个Promise, 那么当前的Promise的状态会由传入的Promise来决定, 相当于状态进行了移交
const newPromise = new Promise((resolve, reject) => {
resolve("aaaa")
})
new Promise((resolve, reject) => {
//状态本来应该是pending -> fulfilled
//但是因为传入的参数是promise,相当于状态进行了移交,
//当前的状态由新的promise决定,现在还是pending,直到新的promise调用了reslove或者reject
//新的promise执行力resolve("aaaa")
//下面的.then里面的东西被自动执行,参数是新的promise里面传来的东西
resolve(newPromise)
}).then(res => {
console.log("res:", res);
}, err => {
console.log("err", err);
})
- 传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口), 那么也会执行该then方法, 并且由该then方法决定后续状态
//传入一个对象, 这个兑现有then方法
new Promise((resolve, reject) => {
const obj = {
then: function(resolve, reject){
resolve("resolve message")
}
}
resolve(obj)
}).then(res => {
console.log("res:", res);
}, err => {
cconsole.log("err:", err);
})
// eatable/runable
const obj = {
eat: function() {
},
run: function() {
}
}
5 Promise的对象方法then
-
then是promise的对象方法,意味这要使用的话,首先得new一个对象,然后调用这个方法。如果是通过类名Promise直接调用一个方法,比如all方法,Promise.all(), 就是类方法,就是不用创建出新对象去访问的方法。也就是类方法是静态方法, 对象方法是在原型上的方法。
-
then方法是Promise对象上的一个方法:它其实是放在Promise的原型上的 Promise.prototype.then
// Promise有哪些对象方法
//console.log(Promise.prototype);
//console.log(Object.getOwnPropertyDescriptors(Promise.prototype))
//直接答应不出来是因为这几个属性的enumerable: false
class Person {
constructor(executor) {
const resolve = () => {
this.callback();
}
const reject = () => {
}
executor(resolve, reject)
}
then(callback) {
this.callback = callback
}
}
const promise = new Promise((resolve, reject) => {
resolve("hahaha")
})
// 1.同一个Promise可以被多次调用then方法
// 当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用
promise.then(res => {
console.log("res1:", res)
})
promise.then(res => {
console.log("res2:", res)
})
promise.then(res => {
console.log("res3:", res)
})
// 2.then方法传入的 "回调函数: 可以有返回值
// then方法本身也是有返回值的, 它的返回值是Promise
// 1> 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值
promise.then(res => {
return "aaaaa"
})
//以上代码相当于
promise.then(res => {
return new Promise((resolve) => {
resolve("aaaaa")
})
})
//因此,接下来在then的话,对应的就是上一个then return的新的promise
promise.then(res => {
return "aaaaa"
}).then(res => {
console.log("res:", res);
return "bbbbb"
})
//如果第一个return没有写ruturn, 默认返回的是undefined
promise.then(res => {
return undefined
}).then(res => {
console.log("res:", res);
return "bbbbb"
})
// 2> 如果我们返回的是一个Promise
promise.then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111111)
}, 3000)
})
}).then(res => {
console.log("res", res);
})
/*
在这里看,是第一个then返回了一个promise
其实是一个看不见的promise包裹着这里写着的promise
return new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111111)
}, 3000)
})
)
})
根据上一节内容,resolve里面有一个新的promise的话,
当前的状态由新的promise决定,现在还是pending,直到新的promise调用了reslove或者reject
所以三秒之后会转为fulfilled,然后执行最后的
(res => {
console.log("res", res);
})
也就是3之后打印111111
*/
// 3> 如果返回的是一个对象, 并且该对象实现了thenable
promise.then(res => {
return {
then: function(resolve, reject) {
resolve(222222)
}
}
}).then(res => {
console.log("res:", res)
})
/*
promise.then(res => {
return {
new Promise(resolve, reject) => {
resolve(
then: function(resolve, reject) {
resolve(222222)
}
)
}
}
}).then(res => {
console.log("res:", res)
})
根据上一节内容,resolve里面有一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口),
那么也会执行该then方法, 并且又该then方法决定后续状态
打印结果是2222
*/
6 Promise的对象方法catch
//const promise = new Promise((resolve, reject) => {
// resolve('success')
//方法1:
//reject("rejected status")
//方法2:也会调用then的第二个参数(rejecte的回调函数)
//throw new Error("rejected status")
// })
// 1.当executor抛出异常时, 也是会调用错误(拒绝)捕获的回调函数的
/* promise.then(undefined, err => {
console.log("err:", err);
}) */
//2.then的第一个参数是回调函数,第二个也是,看起来很容易出问题,阅读性比较不好,分开来比较好
//通过catch方法来传入错误(拒绝)捕获的回调函数,是上面正规方法的语法糖
//不符合promise/a+规范
/* promise.catch(err => {
console.log("err:", err);
}) */
//下面的情况
//这个catch是优先第一个promise的异常捕获,而不是return的新的promise
//如果第一个promise没有异常的话,会留着给下一个用return的新的promise
/* promise.then( res => {
console.log("res:", res);
return '11111'
}).catch(err => {
console.log("err:", err);
}) */
//上面第一个promise是reject("rejected status"),因此会回调
//catch里面的东西,
/*
如果把第1个promise设置成resolve,看catch会不会捕获第二个promise
*/
/* promise.then( res => {
console.log("res:", res);
return new Promise( (resolve, reject) => {
reject('the second promise err status')
})
}).catch( err => {
console.log("err:", err);
}) */
/*
结果是回打印出来err: the second promise err status
*/
// 3.拒绝捕获的问题(前面课程)
const promise = new Promise((resolve, reject) => {
reject("rejected status")
})
//单独写promise.catch的时候就会出现问题
promise.then( res => {
console.log("res", res);
})
/* 如果没有写reject调用时候的处理,也就是回调函数,会报错 */
/* promise.catch( err => {
console.log("err:", err);
})
所以要么按正规写法,then里面放两个回调函数,要么在then之后再调用
catch
*/
//4.catch方法的返回值
//事实上catch方法也是会返回一个Promise对象的,
//所以catch方法后面我们可以继续调用then方法或者catch方法:
//返回的值也是放在新的promise的resolve()作为参数
//所以.catch{return }之后调用的一定是then
const promise = new Promise((resolve, reject) => {
reject("111111")
})
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
return "catch return value"
}).then(res => {
console.log("res result:", res)
}).catch(err => {
console.log("err result:", err)
})
//err: 111111
//res result: catch return value
//如果想让catch后调用下一个catch,需要throw一个error
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
throw "catch return value"
}).then(res => {
console.log("res result:", res)
}).catch(err => {
console.log("err result:", err)
})
//err: 111111
//err result: catch return value
promise.then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err)
throw "catch return value"
}).then(res => {
console.log("res result:", res)
}, err => {
console.log("err2 result:", err)
})
//err: 111111
//err2 result: catch return value
7 Promise的对象方法finally
- finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是reject状态,最终都会
被执行的代码。
- finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行。
const promise = new Promise((resolve, reject) => {
// resolve("resolve message")
reject("reject message")
})
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
}).finally(() => {
console.log("finally code execute")
})
//err: reject message
//finally code execute
8 Promise类方法resolve
119.PNGfunction foo(){
const obj = { name: "why" }
// 想把obj转成转成Promise对象,给别人返回出去
return new Promise((resolve) => {
resolve(obj)
})
}
var result = foo();
result.then( res => {
console.log(res);
})
//{name: 'why'}
//以上的代码我们可以通过Promise.resolve来实现
// 类方法Promise.resolve
// 1.普通的值
const promise = Promise.resolve({ name: "why" })
// 相当于
// const promise2 = new Promise((resolve, reject) => {
// resolve({ name: "why" })
// })
promise.then(res => {
console.log("res:", res)
})
// 2.传入Promise
//和以前一样,旧的promise的状态由新的promise决定
const promise1 = Promise.resolve(new Promise((resolve, reject) => {
resolve("11111")
}))
promise1.then(res => {
console.log("res:", res)
})
// 3.传入thenable对象
9 Promise类方法reject
-
reject方法类似于resolve方法,只是会将Promise对象的状态设置为reject状态。
-
Promise.reject的用法相当于new Promise,只是会调用reject:
// const promise = Promise.reject("rejected message")
// 相当于
// const promise2 = new Promsie((resolve, reject) => {
// reject("rejected message")
// })
// 注意: 无论传入什么值都是一样的
const promise = Promise.reject(new Promise(() => {}))
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
//err: Promise {<pending>}
- Promise.reject传入的参数无论是什么形态,都会直接作为reject状态的参数传递到catch的
10 Promise类方法all
-
它的作用是将多个Promise包裹在一起形成一个新的Promise;
-
新的Promise状态由包裹的所有Promise共同决定:
- 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值
组成一个数组;
- 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// 需求: 所有的Promise都变成fulfilled时, 再拿到结果
Promise.all([p2, p1, p3, "aaaa"]).then(res => {
console.log(res)
}).catch(err => {
console.log("err:", err)
})
//(4) [22222, 11111, 33333, 'aaaa']
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// 意外: 在拿到所有结果之前, 有一个promise变成了rejected, 那么整个promise是rejected
//不会再执行then里面的回调了,而是执行catch里面的回调函数
Promise.all([p2, p1, p3, "aaaa"])
.then(res => {
console.log(res)
}).catch(err => {
console.log("err:", err)
})
//err: 22222
11 Promise类方法allSettled方法
120.PNG// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
Promise.allSettled([p2, p1, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log("err:", err)
})
/*
(3) [{…}, {…}, {…}]
0: {status: 'rejected', reason: 22222}
1: {status: 'fulfilled', value: 11111}
2: {status: 'fulfilled', value: 33333}
length: 3
[[Prototype]]: Array(0)
*/
12 Promise类方法race
如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法:
race是竞技、竞赛的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果;
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// race: 竞技/竞赛
// 只要有一个Promise变成fulfilled状态, 那么就结束
Promise.race([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
//res: 11111
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 4000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// 意外: 如果在所有的promise都还没有结果的情况下,有一个变成rejected了
//就不会等其他结果,直接调用catch回调函数
Promise.race([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
//err: 22222
13 Promise类方法any
any方法是ES12中新增的方法,和race方法是类似的:
-
any方法会等到一个fulfilled状态,才会决定新Promise的状态;
-
如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态;
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 500);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 3000);
})
// any方法
//至少等到有一个结果是fulfilled
Promise.any([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err.errors)
})
//res: 11111
如果所有的Promise都是reject的,那么会报一个AggregateError的错误。
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(11111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222)
}, 500);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(33333)
}, 3000);
})
// any方法
//意外:所有都是rejected的话,会等到所有都执行完之后,最后再执行chatch的回调
Promise.any([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
//err: AggregateError: All promises were rejected
//console.log("err:", err.errors)的话,会返回传入的参数
//err: (3) [11111, 22222, 33333]
网友评论