JS高级—11—Promise
一、为什么需要promise
首先,前端的网络请求需要用这种方式
大家自己写或者调用别人的,需要重写或看源码等导致开发成本过大,ECMA做了统一,于是就有了promise;
出现了promise,只要给调用者返回promise即可,调用者根据promise的状态自己做处理即可;
跟调用者说:我先给你一个承诺即promise,你先去处理其他mainScript,一会就会请求成功后我会调用resolve(data),然后根据规范promise.then()也会被联动调用,这个时候你便可以再promise.then()里接收到数据;
做了统一,方便开发;
二、执行器
1.执行器executor
就是new Promise里的回调函数;
2.resolve传不同值的区别
三、Promise对象(实例)方法
(一)then()方法 课程19集18’’
注意,调用resolve()后会执行then方法的第一个回调函数,这是规定,这些都是ecma的规定,不要问为什么,规定就是这样,v8引擎也就是这样实现的。
1.同一个pormise可以添加多个then方法,然后resolve()时,这多个then方法会同时被调用
2.then方法也会返回一个promise,所以可以链式调用
(1)如果返回一个普通的值(数字/字符串/普通对象/undefined):那么会被包裹到一个新的promise里继续返回,并且这个值作为这个新promise里resolve()方法的参数;
promise.then( (res)=>{ return 'aaaa'; }) 等价于: promise.then( (res)=>{ return new Promise((resolve,reject)=>{ resolve('aaa'); } })
(2)如果返回一个promise,那么这个promise也会被包裹,并且返回的promise的状态,会决定then()方法返回promise的状态;
不明白的话,可以看看上一章中resolve传不同值的状态;
promise.then( (res)=>{ return new Promise((resolve,reject)=>{ resolve(1111); } }) 等价于: promise.then( (res)=>{ return new Promise((resolve,reject)=>{ resolve( new Promise((resolve,reject)=>{ resolve(1111); }); } })
(3)如果返回一个对象,并且这个对象实现了then方法接口
promise.then( (res)=>{ return { then: ()=> resolve(11111) } }) 等价于: promise.then( (res)=>{ return new Promise((resolve,reject)=>{ resolve( obj.then()); } })
3.then()方法可以传递两个参数,第一个参数时resolve()执行的,第二个参数时reject()执行的;
但是现在一般都是只写一个参数,第二个参数不写,rejected要执行的回调放在catch方法中;
function* foo() { console.log(111) yield bar(); console.log(333) } function bar() { console.log(2222) return Promise.resolve('helloworld'); } generator = foo(); generator.next() console.log(444) 结果是1111 2222 4444 所以说:yied和await一样的,也是会去执行后面跟着的函数里的代码的,比如这里就是执行了bar()里的222; 只是遇到了resolve后才会让出控制权,等promise变成fullfilled态后会继续拿到js线程的控制权; yield bar() 和bar() 所以说:yield和单纯的promise.then()方法还是不一样的,.then()方法会把回调函数加入到微任务队列,然后继续执行本函数下面的代码,但如果是yield,则不仅.then()方法会把回调函数加入到微任务队列,yield地下的代码也无法执行也应该被加入到微任务队列了; 至于原因,那可能就是ecma的规范如此定义的;
(二)catch方法
(三)finally方法
四、Promise类方法
(一)resolve方法
(二)reject方法
(三)all方法
1.感觉很像数组的every方法,都要满足时才为true;
2.all方法会返回一个新的prmose,所以Promise.all()后面还可以跟promise专属的then()方法,当这个新的promise实力为fullfilled状态时就是执行这个then()方法;
3.后面跟then()方法,then方法的参数res是一个数组,数组里是所有promise的resolve(回调值)的回调值,数组元素的顺序就是all()方法里参数的顺序;
解决办法就是加async和await;
(四)allSettled方法
(五)race方法
(六)any方法
all方法类似every,那么any方法就类似some方法,只要有一个就为true;
五、手写promise源码
es6的源码promise参考promise/A+规范,我们可以根据规范自己实现一个promise类;
// ES6 ES2015 // https://promisesaplus.com/ const PROMISE_STATUS_PENDING = 'pending' const PROMISE_STATUS_FULFILLED = 'fulfilled' const PROMISE_STATUS_REJECTED = 'rejected' // 工具函数 function execFunctionWithCatchError(execFn, value, resolve, reject) { try { const result = execFn(value) resolve(result) } catch(err) { reject(err) } } class HYPromise { constructor(executor) { this.status = PROMISE_STATUS_PENDING this.value = undefined this.reason = undefined this.onFulfilledFns = [] this.onRejectedFns = [] const resolve = (value) => { if (this.status === PROMISE_STATUS_PENDING) { //我们知道什么时候在执行器中调用resolve()很重要,比如说网络请求,那么肯定是要网络请求好了比如浏览器的其他线程把建立连接传数据断开连接都做完了,才会把resolve()执行才会把回到函数加入到微任务队列;
//当然我们自己测试可能会立即调用resolve,然后then()内的回调函数会立马加入到微任务队列里; // 添加微任务 queueMicrotask(() => { this.status = PROMISE_STATUS_FULFILLED this.value = value this.onFulfilledFns.forEach(fn => { fn(this.value) }) }); } } const reject = (reason) => { if (this.status === PROMISE_STATUS_PENDING) { // 添加微任务 queueMicrotask(() => { this.status = PROMISE_STATUS_REJECTED this.reason = reason this.onRejectedFns.forEach(fn => { fn(this.reason) }) }) } } try { executor(resolve, reject) //立即执行函数(传递进来后立即执行) } catch (err) { reject(err) } } then(onFulfilled, onRejected) { const defaultOnRejected = err => { throw err } onRejected = onRejected || defaultOnRejected const defaultOnFulfilled = value => { return value } onFulfilled = onFulfilled || defaultOnFulfilled return new HYPromise((resolve, reject) => { // 1.如果在then调用的时候, 状态已经确定下来 if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) { execFunctionWithCatchError(onFulfilled, this.value, resolve, reject) } if (this.status === PROMISE_STATUS_REJECTED && onRejected) { execFunctionWithCatchError(onRejected, this.reason, resolve, reject) } // 2.将成功回调和失败的回调放到数组中 if (this.status === PROMISE_STATUS_PENDING) { if (onFulfilled) this.onFulfilledFns.push(() => { execFunctionWithCatchError(onFulfilled, this.value, resolve, reject) }) if (onRejected) this.onRejectedFns.push(() => { execFunctionWithCatchError(onRejected, this.reason, resolve, reject) }) } }) } catch(onRejected) { return this.then(undefined, onRejected) } finally(onFinally) { this.then(() => { onFinally() }, () => { onFinally() }) } //加statuc关键字表示类方法; static resolve(value) { return new HYPromise((resolve) => resolve(value)) } static reject(reason) { return new HYPromise((resolve, reject) => reject(reason)) } static all(promises) { // 问题关键: 什么时候要执行resolve, 什么时候要执行reject return new HYPromise((resolve, reject) => { const values = [] promises.forEach(promise => { promise.then(res => { values.push(res) if (values.length === promises.length) { resolve(values) } }, err => { reject(err) }) }) }) } static allSettled(promises) { return new HYPromise((resolve) => { const results = [] promises.forEach(promise => { promise.then(res => { results.push({ status: PROMISE_STATUS_FULFILLED, value: res}) if (results.length === promises.length) { resolve(results) } }, err => { results.push({ status: PROMISE_STATUS_REJECTED, value: err}) if (results.length === promises.length) { resolve(results) } }) }) }) } static race(promises) { return new HYPromise((resolve, reject) => { promises.forEach(promise => { // promise.then(res => { // resolve(res) // }, err => { // reject(err) // }) promise.then(resolve, reject) }) }) } static any(promises) { // resolve必须等到有一个成功的结果 // reject所有的都失败才执行reject const reasons = [] return new HYPromise((resolve, reject) => { promises.forEach(promise => { promise.then(resolve, err => { reasons.push(err) if (reasons.length === promises.length) { reject(new AggregateError(reasons)) } }) }) }) } } const p1 = new Promise((resolve, reject) => { setTimeout(() => { reject(1111) }, 3000) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject(2222) }, 2000) }) const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject(3333) }, 3000) }) // HYPromise.race([p1, p2, p3]).then(res => { // console.log("res:", res) // }).catch(err => { // console.log("err:", err) // }) HYPromise.any([p1, p2, p3]).then(res => { console.log("res:", res) }).catch(err => { console.log("err:", err.errors) })