Javascript高级篇 - 异步编程保姆级教程 Promise Async Await generator

ops/2024/11/19 14:40:55/

Promise_0">Promise

1)状态不可逆性:

javascript">let p1 = new Promise((resolve, reject) => {resolve('success')reject('fail')
})
console.log('p1', p1)let p2 = new Promise((resolve, reject) => {reject('success')resolve('fail')
})
console.log('p2', p2)let p3 = new Promise((resolve, reject) => {throw('error')
})
console.log('p3', p3)

在这里插入图片描述
这里说明了Promise的四个特点:

  1. 执行了resolve,Promise状态会变成fulfilled;
  2. 执行了reject,Promise状态会变成rejected;
  3. Promise状态不可逆,第一次成功就永久为fulfilled,第一次失败就永远状态为rejected;
  4. Promise中有throw的话,就相当于执行了reject;

Promise_28">手动实现 Promise

  1. Promise的初始状态是pending;
  2. 需要对resolve和reject绑定this:确保resolve和reject的this指向永远指向当前的MyPromise实例,防止随着函数执行环境的改变而改变;
javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向 this.initBind()// 执行传进来的函数executor(this.resolve, this.reject)}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态}resolve(value) {// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}
}

测试结果

javascript">const test1 = new MyPromise((resolve, reject) => {resolve('success')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: 'success' }const test2 = new MyPromise((resolve, reject) => {reject('fail')
})
console.log(test2) // MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }

状态不可变

javascript">const test1 = new MyPromise((resolve, reject) => {resolve('success')reject('fail')
})
console.log(test1) // MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }

正确的应该是状态为fulfilled,但这里状态又变成了rejected。
Promise有三种状态:

  • pending:等待中,是初始状态;
  • fulfilled:成功状态;
  • rejected:失败状态;
    一旦状态从pending变为fulfilled或者rejected,那么此Promise实例的状态就不可以改变了。
    在这里插入图片描述
    然后我们来改进一下:
javascript">    resolve(value) {// state是不可变的
+        if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// state是不可变的
+        if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}

继续测试一下:

javascript">const test1 = new MyPromise((resolve, reject) => {// 只以第一次为准resolve('success')reject('fail')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: 'success' }

Throw处理

在这里插入图片描述
Promise中如果有throw的话 就会执行Reject操作,加上try catch包一下:

javascript">+        try {// 执行传进来的函数executor(this.resolve, this.reject)
+        } catch (e) {// 捕捉到错误直接执行reject
+            this.reject(e)
+        }

继续完善下:

javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}
}

测试代码

javascript">const test3 = new MyPromise((resolve, reject) => {throw('fail')
})
console.log(test3) // MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }

then

一般使用方式如下:

javascript">// 马上输出 ”success“
const p1 = new Promise((resolve, reject) => {resolve('success')
}).then(res => console.log(res), err => console.log(err))// 1秒后输出 ”fail“
const p2 = new Promise((resolve, reject) => {setTimeout(() => {reject('fail')}, 1000)
}).then(res => console.log(res), err => console.log(err))// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {resolve(100)
}).then(res => 2 * res, err => console.log(err)).then(res => console.log(res), err => console.log(err))

根据上述代码可以确定:

  1. then接收两个回调,一个是成功回调,一个是失败回调;
  2. Promise状态为fulfilled执行成功回调,为rejected执行失败回调;
  3. 如resolve或reject在定时器里,则定时器结束后再执行then;
  4. then支持链式调用,下一次then执行受上一次then返回值的影响;

实现then函数

javascript">    then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)}}

完善一下上边的code:

javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)}}
}

测试then的效果:

javascript">// 输出 ”success“
const test = new MyPromise((resolve, reject) => {resolve('success')
}).then(res => console.log(res), err => console.log(err))

定时器

如何保证执行then回调在有定时器的情况下:

javascript">// 1秒后输出 ”fail“
const p2 = new Promise((resolve, reject) => {setTimeout(() => {reject('fail')}, 1000)
}).then(res => console.log(res), err => console.log(err))

我们不能确保1秒后才执行then函数,但是我们可以保证1秒后再执行then里的回调(后续课程的事件循环会讲到)
在这里插入图片描述
在这1秒时间内,我们可以先把then里的两个回调保存起来,然后等到1秒过后,执行了resolve或者reject,咱们再去判断状态,并且判断要去执行刚刚保存的两个回调中的哪一个回调。
那么问题来了,我们怎么知道当前1秒还没走完甚至还没开始走呢?其实很好判断,只要状态是pending,那就证明定时器还没跑完,因为如果定时器跑完的话,那状态肯定就不是pending,而是fulfilled或者rejected
那是用什么来保存这些回调呢?建议使用数组,因为一个promise实例可能会多次then,用数组就一个一个保存了

javascript">    initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态
+        this.onFulfilledCallbacks = [] // 保存成功回调
+        this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调
+        while (this.onFulfilledCallbacks.length) {
+            this.onFulfilledCallbacks.shift()(this.PromiseResult)
+        }}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调
+        while (this.onRejectedCallbacks.length) {
+            this.onRejectedCallbacks.shift()(this.PromiseResult)
+        }}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)
+        } else if (this.PromiseState === 'pending') {
+            // 如果状态为待定状态,暂时保存两个回调
+            this.onFulfilledCallbacks.push(onFulfilled.bind(this))
+            this.onRejectedCallbacks.push(onRejected.bind(this))
+        }}

完整代码:

javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态this.onFulfilledCallbacks = [] // 保存成功回调this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.PromiseResult)}}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.PromiseResult)}}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调onFulfilled(this.PromiseResult)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二哥回调onRejected(this.PromiseResult)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(onFulfilled.bind(this))this.onRejectedCallbacks.push(onRejected.bind(this))}}
}

测试下定时器是否可以正常调用

javascript">const test2 = new MyPromise((resolve, reject) => {setTimeout(() => {resolve('success') // 1秒后输出 success}, 1000)
}).then(res => console.log(res), err => console.log(err))

Promise_493">Promise的链式调用

then支持链式调用,下一次then执行受上一次then返回值的影响,给大家举个例子:

javascript">// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {resolve(100)
}).then(res => 2 * res, err => console.log(err)).then(res => console.log(res), err => console.log(err))// 链式调用 输出300
const p4 = new Promise((resolve, reject) => {resolve(100)
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err => console.log(err)).then(res => console.log(res), err => console.log(err))

根据上文,可以得到:

  1. then方法本身会返回一个新的Promise对象;
  2. 如果返回值是promise对象,返回值为成功,新promise就是成功;
  3. 如果返回值是promise对象,返回值为失败,新promise就是失败;
  4. 如果返回值非promise对象,新promise对象就是成功,值为此返回值;

then是Promise上的方法,那如何实现then完还能再then呢?
then执行后返回一个Promise对象就行了,就能保证then完还能继续执行then;
在这里插入图片描述

javascript">    then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }var thenPromise = new MyPromise((resolve, reject) => {const resolvePromise = cb => {try {const x = cb(this.PromiseResult)if (x === thenPromise && x) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}}if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调resolvePromise(onFulfilled)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二个回调resolvePromise(onRejected)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))}})// 返回这个包装的Promisereturn thenPromise}

完整代码为:

javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态this.onFulfilledCallbacks = [] // 保存成功回调this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.PromiseResult)}}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.PromiseResult)}}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }var thenPromise = new MyPromise((resolve, reject) => {const resolvePromise = cb => {try {const x = cb(this.PromiseResult)if (x === thenPromise) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}}if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调resolvePromise(onFulfilled)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二个回调resolvePromise(onRejected)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))}})// 返回这个包装的Promisereturn thenPromise}
}

测试一下看看~

javascript">const test3 = new MyPromise((resolve, reject) => {resolve(100) // 输出 状态:success 值: 200
}).then(res => 2 * res, err => 3 * err).then(res => console.log('success', res), err => console.log('fail', err))const test4 = new MyPromise((resolve, reject) => {resolve(100) // 输出 状态:fail 值:200}).then(res => new MyPromise((resolve, reject) => reject(2 * res)), err => new Promise((resolve, reject) => resolve(3 * err))).then(res => console.log('success', res), err => console.log('fail', err))

执行顺序问题

这里需要了解,then方法是微任务(后续课程会讲)

javascript">const p = new Promise((resolve, reject) => {resolve(1)
}).then(res => console.log(res), err => console.log(err))console.log(2)输出顺序是 2 1

具体原因如下:
在 JavaScript 中,Promise 是异步执行的,而 console.log 是同步执行的。当你创建一个新的 Promise 并立即调用 resolve 方法时,Promise 的状态会立即变为 “fulfilled”,但是 Promise 的处理函数(.then 中的回调函数)会被放入微任务队列(microtask queue)中,等待当前执行栈清空后执行。
由于 **JavaScript 运行时会先执行所有同步代码,然后才处理微任务队列中的微任务,所以 console.log(2) 会先于 .then 中的回调函数执行。**因此,输出顺序是 2 然后是 1。
这是 JavaScript 事件循环(Event Loop)的一个基本特性,它确保了同步代码的即时执行,而异步代码(如 Promise 的回调函数)则在微任务队列中等待执行。

至此可以采用类似setTimeout来解决类似问题:

javascript">const resolvePromise = cb => {setTimeout(() => {try {const x = cb(this.PromiseResult)if (x === thenPromise) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}})
}

完整Code:

javascript">class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.reject(e)}}initBind() {// 初始化thisthis.resolve = this.resolve.bind(this)this.reject = this.reject.bind(this)}initValue() {// 初始化值this.PromiseResult = null // 终值this.PromiseState = 'pending' // 状态this.onFulfilledCallbacks = [] // 保存成功回调this.onRejectedCallbacks = [] // 保存失败回调}resolve(value) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行resolve,状态变为fulfilledthis.PromiseState = 'fulfilled'// 终值为传进来的值this.PromiseResult = value// 执行保存的成功回调while (this.onFulfilledCallbacks.length) {this.onFulfilledCallbacks.shift()(this.PromiseResult)}}reject(reason) {// state是不可变的if (this.PromiseState !== 'pending') return// 如果执行reject,状态变为rejectedthis.PromiseState = 'rejected'// 终值为传进来的reasonthis.PromiseResult = reason// 执行保存的失败回调while (this.onRejectedCallbacks.length) {this.onRejectedCallbacks.shift()(this.PromiseResult)}}then(onFulfilled, onRejected) {// 接收两个回调 onFulfilled, onRejected// 参数校验,确保一定是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => valonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }var thenPromise = new MyPromise((resolve, reject) => {const resolvePromise = cb => {setTimeout(() => {try {const x = cb(this.PromiseResult)if (x === thenPromise) {// 不能返回自身哦throw new Error('不能返回自身。。。')}if (x instanceof MyPromise) {// 如果返回值是Promise// 如果返回值是promise对象,返回值为成功,新promise就是成功// 如果返回值是promise对象,返回值为失败,新promise就是失败// 谁知道返回的promise是失败成功?只有then知道x.then(resolve, reject)} else {// 非Promise就直接成功resolve(x)}} catch (err) {// 处理报错reject(err)throw new Error(err)}})}if (this.PromiseState === 'fulfilled') {// 如果当前为成功状态,执行第一个回调resolvePromise(onFulfilled)} else if (this.PromiseState === 'rejected') {// 如果当前为失败状态,执行第二个回调resolvePromise(onRejected)} else if (this.PromiseState === 'pending') {// 如果状态为待定状态,暂时保存两个回调// 如果状态为待定状态,暂时保存两个回调this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))}})// 返回这个包装的Promisereturn thenPromise}
}

其他方法:
all:

  1. 接收一个Promise数组,数组中如有非Promise项,则此项当做成功;
  2. 如果所有Promise都成功,则返回成功结果数组;
  3. 如果有一个Promise失败,则返回这个失败结果;
javascript">static all(promises) {const result = []let count = 0return new MyPromise((resolve, reject) => {const addData = (index, value) => {result[index] = valuecount++if (count === promises.length) resolve(result)}promises.forEach((promise, index) => {if (promise instanceof MyPromise) {promise.then(res => {addData(index, res)}, err => reject(err))} else {addData(index, promise)}})})
}

race

  1. 接收一个Promise数组,数组中如有非Promise项,则此项当做成功;
  2. 哪个Promise最快得到结果,就返回那个结果,无论成功失败;
javascript">static race(promises) {return new MyPromise((resolve, reject) => {promises.forEach(promise => {if (promise instanceof MyPromise) {promise.then(res => {resolve(res)}, err => {reject(err)})} else {resolve(promise)}})})
}

allSettled
3. 接收一个Promise数组,数组中如有非Promise项,则此项当做成功;
4. 把每一个Promise的结果,集合成数组后返回;

javascript">static allSettled(promises) {return new Promise((resolve, reject) => {const res = []let count = 0const addData = (status, value, i) => {res[i] = {status,value}count++if (count === promises.length) {resolve(res)}}promises.forEach((promise, i) => {if (promise instanceof MyPromise) {promise.then(res => {addData('fulfilled', res, i)}, err => {addData('rejected', err, i)})} else {addData('fulfilled', promise, i)}})})
}

any

与all相反

  1. 接收一个Promise数组,数组中如有非Promise项,则此项当做成功;
  2. 如果有一个Promise成功,则返回这个成功结果;
  3. 如果所有Promise都失败,则报错;
javascript">static any(promises) {return new Promise((resolve, reject) => {let count = 0promises.forEach((promise) => {promise.then(val => {resolve(val)}, err => {count++if (count === promises.length) {reject(new AggregateError('All promises were rejected'))}})})})
}
}

Promise_A__973">Promise A+ 规范

上文我们实现了简版的Promise,接下来看下标准的Promise/A+的规范
官方链接
对照的翻译如下:
一个开放、健全且通用的 JavaScript Promise 标准。由开发者制定,供开发者参考。
一个 promise 表示异步操作的最终结果。与 promise 进行交互的主要方式是通过它的方法 then。该方法通过注册回调来得到这个 promise 的最终 value ,或者为什么这个 promise 不能被 fulfilled 的 reason 。
该规范详细说明了 then 方法的行为,提供了一个可互操作的基础,因此所有符合 Promises/A+ 的 promise 实现都可以依赖该基础。尽管 Promises/A+ 组织可能偶尔会通过向后兼容的微小更改来修改此规范,以解决新发现的情况,但我们只有在仔细考虑、讨论和测试后才会进行大的或向后不兼容的更改。因此, 该规范应该被认为是十分稳定的 。
从历史上看, Promises/A+ 阐明了早期 Promises/A proposal 的条款,并将部分事实上已经实现的拓展涵盖其中,以及对某些未指定或者有问题的部分省略。
最后,Promises/A+ 规范的核心不包括:如何 create 、fulfill 或 reject promises。而是选择专注于提供可互操作的 then 方法。不过伴随规范的未来工作可能会涉及这些主题。
这里可以看到,Promises/A+ 规范目前的核心是规范 then 方法,并没有对如何实现 promise 以及如何改变 promise 的状态进行限制。

常见术语

  1. “prmoise” 是一个拥有符合本规范的 then 方法的对象或者函数;
  2. “thenable” 是一个定义了 then 方法的对象或者函数;
  3. “value” 是 JavaScript 的任意合法值(包括 undefined, thenable, promise);
  4. “exception” 是一个用 throw 语句抛出的 value ;
  5. “reason” 是一个表示 promise 被 rejected 的 value ;

Promise 状态:
pormise 必须是以下三个状态之一: pending, fulfilled, rejected。

  • 当 promise 处于 pending 状态时:
    • 可以转换到 fulfilled 或 rejected 状态;
  • 当 promise 处于 fulfilled 状态时:
    • 不能转换到其他状态;
    • 必须有一个 value ,并且不能改变;
  • 当 promise 处于 rejected 状态时:
    • 不能转换到其他状态;
    • 必须有 reason ,并且不能改变;

promise 必须提供一个 then 方法,能由此去访问当前或最终的 value 或者 reason 。pormise 的 then 方法, 接受两个参数

  • onFulfilled 和 onRejected 都是可选参数:
    • 如果 onFulfilled 不是函数,则忽略;
    • 如果 onRejected 不是函数,则忽略;
  • 如果 onFulfilled 是一个函数:
    • 它必须在 promise 被 fulfilled 后,以 promise 的 value 作为第一个参数调用;
    • 它不能在 promise 被 fulfilled 之前调用;
    • 它不能被调用多次;
  • 如果 onRejected 是一个函数:
    • 它必须在 promise 被 rejected 后,以 promise 的 reason 作为第一个参数调用;
    • 它不能能在 promise 被 rejected 之前调用;
    • 它不能被调用多次;
    • 在 execution context 栈(执行上下文栈)只包含平台代码之前, onFulfilled 或者 onRejected 不能被调用 (译者注: 异步执行回调);
  • onFulfilled 或者 onRejected 必须以函数形式调用(即不能有this值)
  • then 方法可以被同一个 promise 调用多次
    • 如果或者当 promise 处于 fulfilled 状态, 所有自己的 onFulfilled 回调函数,必须要按照 then 注册的顺序被调用;
    • 如果或者当 promise 处于 rejected 状态, 所有自己的 onRejected 回调函数,必须要按照 then 注册的顺序被调用;
  • then 方法必须要返回 promise
    • 如果 onFulfilled 或者 onRejected 返回一个值 x ,则执行 Promise Resolution Procedure:[[Resolve]](promise2, x);
  • 如果 onFulfilled 或者 onRejected 抛出异常 e , promise2 必须以 e 作为 reason ,转到 rejected 状态;
  • 如果 onFulfilled 不是函数,并且 promise1 处于 fulfilled 状态 ,则 promise2 必须以与 promise1 同样的 value 被 fulfilled;
  • 如果 onRejected 不是函数,并且 promise1 处于 rejected 状态 ,则 promise2 必须以与 promise1 同样的 reason 被 rejected;

Promise Resolution Procedure:
Promise Resolution Procedure 是一个抽象操作。它以一个 promise 和一个 value 作为输入,记作:[[Resolve]](promise, x) 。 如果 x 是一个 thenable , 它会尝试让 promise 变成与 x 的一样状态 ,前提 x 是一个类似的 promise 对象。否则,它会让 promise 以 x 作为 value 转为 fulfilled 状态。
这种对 thenables 的处理允许不同的 promise 进行互操作,只要它们暴露一个符合 Promises/A+ 的 then 方法。它还允许 Promises/A+ 实现使用合理的 then 方法“同化”不一致的实现。
[[Resolve]](promise, x) 执行以下步骤:

  • 如果 promise 和 x 引用的是同一个对象,则以一个 TypeError 作为 reason 让 promise 转为 rejeted 状态;
  • 如果 x 也是一个 promise ,则让 promise 接受它的状态:
    • 如果 x 处于 pending 状态,promise 必须保持 pending 状态,直到 x 变成 fulfilled 或者 rejected 状态,promise 才同步改变;
    • 如果或者当 x 处于 fulfilled 状态, 以同样的 value 让 promise 也变成 fulfilled 状态;
    • 如果或者当 x 处于 rejected 状态, 以同样的 reason 让 promise 也变成 rejected 状态;
  • 如果 x 是一个对象或者函数:
    • 令 then 等于 x.then;
    • 如果读取 x.then 抛出异常 e , 以 e 作为 reason 让 promise 变成 rejected 状态;
    • 如果 then 是一个函数,以 x 作为 this 调用它,传入第一个参数 resolvePromise , 第二个参数 rejectPromise
      • 如果 resolvePromise 被传入 y 调用, 则执行 [[Resolve]](promise, y);
      • 如果 rejectedPromise 被传入 r 调用,则用,r 作为 reason 让 promise 变成 rejected 状态;
      • 如果 resolvePromise 和 rejectPromise 都被调用了,或者被调用多次了。只有第一次调用生效,其余会被忽略;
      • 如果调用 then 抛出异常 e:
        • 如果 resolvepromise 或 rejectPromise 已经被调用过了,则忽略它;
        • 否则, 以 e 作为 reason 让 promise 变成 rejected 状态;
      • 如果 then 不是一个函数,以 x 作为 value 让 promise 变成 fulfilled 状态;
  • 如果 x 不是对象或函数, 以 x 作为 value 让 promise 变成 fulfilled 状态;
    如果一个 promise 被一个循环的 thenable 链中的对象 resolved,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励实现者检测这样的递归是否存在,并且以 TypeError 作为 reason 拒绝 promise;

Async__Await_1050">Async & Await

async/await的用处:用同步方式,执行异步操作

javascript">function request(num) { // 模拟接口请求return new Promise(resolve => {setTimeout(() => {resolve(num * 2)}, 1000)})
}request(1).then(res1 => {console.log(res1) // 1秒后 输出 2request(2).then(res2 => {console.log(res2) // 2秒后 输出 4})
})

现在有一个新的要求:先请求完接口1,再拿接口1返回的数据,去当做接口2的请求参数,那我们也可以这么做:

javascript">request(5).then(res1 => {console.log(res1) // 1秒后 输出 10request(res1).then(res2 => {console.log(res2) // 2秒后 输出 20})
})

如果嵌套的多了,这个时候就可以用async/await来解决了:

javascript">async function fn () {const res1 = await request(5)const res2 = await request(res1)console.log(res2) // 2秒后输出 20
}
fn()

使用async/await代替上述的内容:

javascript">async function fn () {await request(1)await request(2)// 2秒后执行完
}
fn()

在async函数中,await规定了异步操作只能一个一个排队执行,从而达到用同步方式,执行异步操作的效果.

注意:await只能在async函数中使用

刚刚上面的例子await后面都是跟着异步操作Promise,那如果不接Promise

javascript">function request(num) { // 去掉PromisesetTimeout(() => {console.log(num * 2)}, 1000)
}async function fn() {await request(1) // 2await request(2) // 4// 1秒后执行完  同时输出
}
fn()

?: 什么是async?async是一个位于function之前的前缀,只有async函数中,才能使用await。那async执行完是返回是什么

javascript">async function fn () {}
console.log(fn) // [AsyncFunction: fn]
console.log(fn()) // Promise {<fulfilled>: undefined}

可以看出,async函数执行完会自动返回一个状态为fulfilled的Promise,也就是成功状态,但是值却是undefined,那要怎么才能使值不是undefined呢?只要函数有return返回值就行了。

javascript">async function fn (num) {return num
}
console.log(fn) // [AsyncFunction: fn]
console.log(fn(10)) // Promise {<fulfilled>: 10}
fn(10).then(res => console.log(res)) // 10

总结:

  1. await只能在async函
  2. 数中使用,不然会报错;
  3. async函数返回的是一个Promise对象,有无值看有无return值;
  4. await后面最好是接Promise,虽然接其他值也能达到排队效;
  5. async/await作用是用同步方式,执行异步操作

Q:async/await是一种语法糖,那么什么是语法糖呢?
A:语法糖是简化代码的一种方式,用其他方式也能达到同样的效果,但写法可能没有这么便利。套了一层外衣
ES6的class也是语法糖,因为其实用普通function也能实现同样效果
回归正题,async/await是一种语法糖,用到的是ES6里的迭代函数——generator函数

Generator_1147">Generator:

介绍
generator函数跟普通函数在写法上的区别就是,多了一个星号*,并且只有在generator函数中才能使用yield,而yield相当于generator函数执行的中途暂停点,比如下方有3个暂停点。而怎么才能暂停后继续走呢?那就得使用到next方法,next方法执行后会返回一个对象,对象中有value 和 done两个属性

  • value:暂停点后面接的值,也就是yield后面接的值;
  • done:是否generator函数已走完,没走完为false,走完为true;
javascript">function* gen() {yield 1yield 2yield 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }

可以看到最后一个是undefined,这取决于你generator函数有无返回值

javascript">function* gen() {yield 1yield 2yield 3return 4
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: 4, done: true }

Yeild 后接函数:
yield后面接函数的话,到了对应暂停点yield,会马上执行此函数,并且该函数的执行返回值,会被当做此暂停点对象的value

javascript">function fn(num) {console.log(num)return num
}
function* gen() {yield fn(1)yield fn(2)return 3
}
const g = gen()
console.log(g.next()) 
// 1
// { value: 1, done: false }
console.log(g.next())
// 2
//  { value: 2, done: false }
console.log(g.next()) 
// { value: 3, done: true }

yield后接promise

javascript">function fn(num) {return new Promise(resolve => {setTimeout(() => {resolve(num)}, 1000)})
}
function* gen() {yield fn(1)yield fn(2)return 3
}
const g = gen()
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: 3, done: true }

如果想获取的是两个Promise的结果1 和 2,可以使用Promise的then

javascript">const g = gen()
const next1 = g.next()
next1.value.then(res1 => {console.log(next1) // 1秒后输出 { value: Promise { 1 }, done: false }console.log(res1) // 1秒后输出 1const next2 = g.next()next2.value.then(res2 => {console.log(next2) // 2秒后输出 { value: Promise { 2 }, done: false }console.log(res2) // 2秒后输出 2console.log(g.next()) // 2秒后输出 { value: 3, done: true }})
})

Next传递参数

generator函数可以用next方法来传参,并且可以通过yield来接收这个参数,注意两点

  1. 第一次next传参是没用的,只有从第二次开始next传参才有用;
  2. next传值时,要记住顺序是,先右边yield,后左边接收参数;
javascript">function* gen() {const num1 = yield 1console.log(num1)const num2 = yield 2console.log(num2)return 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next(11111))
// 11111
//  { value: 2, done: false }
console.log(g.next(22222)) 
// 22222
// { value: 3, done: true }

Promisenext_1263">Promise&next传参

根据上文可以知道:

  1. yield后面接Promise
  2. next函数传参;
    所以一起使用时的效果为:
javascript">function fn(nums) {return new Promise(resolve => {setTimeout(() => {resolve(nums * 2)}, 1000)})
}
function* gen() {const num1 = yield fn(1)const num2 = yield fn(num1)const num3 = yield fn(num2)return num3
}
const g = gen()
const next1 = g.next()
next1.value.then(res1 => {console.log(next1) // 1秒后同时输出 { value: Promise { 2 }, done: false }console.log(res1) // 1秒后同时输出 2const next2 = g.next(res1) // 传入上次的res1next2.value.then(res2 => {console.log(next2) // 2秒后同时输出 { value: Promise { 4 }, done: false }console.log(res2) // 2秒后同时输出 4const next3 = g.next(res2) // 传入上次的res2next3.value.then(res3 => {console.log(next3) // 3秒后同时输出 { value: Promise { 8 }, done: false }console.log(res3) // 3秒后同时输出 8// 传入上次的res3console.log(g.next(res3)) // 3秒后同时输出 { value: 8, done: true }})})
})

实现async await

上方的generator函数的Promise&next传参,就很像async/await了,区别在于

  1. gen函数执行返回值不是Promise,asyncFn执行返回值是Promise
  2. gen函数需要执行相应的操作,才能等同于asyncFn的排队效果;
  3. gen函数执行的操作是不完善的,因为并不确定有几个yield,不确定会嵌套几次;

针对这种情况,可以通过高阶函数(HOC)封装:
高阶函数:参数是函数,返回值也可以是函数。

javascript">function highorderFn(函数) {// 一系列处理return 函数
}

根据上述代码,可以封装一个高阶函数,接收一个generator函数,并经过处理,返回一个具有async函数功能的函数:

function generatorToAsync(generatorFn) {// 经过一系列处理return 具有async函数功能的函数
}

返回值是Promise:

function* gen() {}const asyncFn = generatorToAsync(gen)console.log(asyncFn()) // 期望这里输出 Promise
javascript">function* gen() {}
function generatorToAsync (generatorFn) {return function () {return new Promise((resolve, reject) => {})}
}const asyncFn = generatorToAsync(gen)console.log(asyncFn()) // Promise

结合以上代码

把之前的处理代码,加入generatorToAsync函数中

javascript">function fn(nums) {return new Promise(resolve => {setTimeout(() => {resolve(nums * 2)}, 1000)})
}
function* gen() {const num1 = yield fn(1)const num2 = yield fn(num1)const num3 = yield fn(num2)return num3
}
function generatorToAsync(generatorFn) {return function () {return new Promise((resolve, reject) => {const g = generatorFn()const next1 = g.next()next1.value.then(res1 => {const next2 = g.next(res1) // 传入上次的res1next2.value.then(res2 => {const next3 = g.next(res2) // 传入上次的res2next3.value.then(res3 => {// 传入上次的res3resolve(g.next(res3).value)})})})})}
}const asyncFn = generatorToAsync(gen)asyncFn().then(res => console.log(res)) // 3秒后输出 8

到这里,就已经实现了async/await的初始功能了

javascript">async function asyncFn() {const num1 = await fn(1)const num2 = await fn(num1)const num3 = await fn(num2)return num3
}
asyncFn().then(res => console.log(res)) // 3秒后输出 8

因为async中可以支持若干个await,await的个数是不确定的。同样类比,generator函数中,也可能有2个yield,3个yield,5个yield,所以需要对上述代码进行改造

javascript">function generatorToAsync(generatorFn) {return function() {const gen = generatorFn.apply(this, arguments) // gen有可能传参// 返回一个Promisereturn new Promise((resolve, reject) => {function go(key, arg) {let restry {res = gen[key](arg) // 这里有可能会执行返回reject状态的Promise} catch (error) {return reject(error) // 报错的话会走catch,直接reject}// 解构获得value和doneconst { value, done } = resif (done) {// 如果done为true,说明走完了,进行resolve(value)return resolve(value)} else {// 如果done为false,说明没走完,还得继续走// value有可能是:常量,PromisePromise有可能是成功或者失败return Promise.resolve(value).then(val => go('next', val), err => go('throw', err))}}go("next") // 第一次执行})}
}const asyncFn = generatorToAsync(gen)asyncFn().then(res => console.log(res))

测试结果:

javascript">async function asyncFn() {const num1 = await fn(1)console.log(num1) // 2const num2 = await fn(num1)console.log(num2) // 4const num3 = await fn(num2)console.log(num3) // 8return num3
}
const asyncRes = asyncFn()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8
javascript">function* gen() {const num1 = yield fn(1)console.log(num1) // 2const num2 = yield fn(num1)console.log(num2) // 4const num3 = yield fn(num2)console.log(num3) // 8return num3
}const genToAsync = generatorToAsync(gen)
const asyncRes = genToAsync()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8

http://www.ppmy.cn/ops/134999.html

相关文章

汽车资讯新动力:Spring Boot技术驱动

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

【青牛科技】D7312带 ALC 双通道前置放大器电路

概述&#xff1a; D7312是一块双极性集 成电路。可用于小型收录机中作前置放大电路。 主要特点&#xff1a; ● 含ALC电路和ALC检波电路。 ● 外接元件少。 ● 增益高&#xff0c;噪声低。 ● 静态电流小 ● 电源开关冲击噪音小、 反应快 ● 具有过热保护功能 ● 封装形式&…

【Python绘图】两种绘制混淆矩阵的方式 (ConfusionMatrixDisplay(), imshow()) 以及两种好看的colorbar

在机器学习领域&#xff0c;混淆矩阵是一个评估分类模型性能的重要工具。它不仅展示了模型预测的准确性&#xff0c;还揭示了模型在不同类别上的表现。本文介绍两种在Python中绘制混淆矩阵的方法&#xff1a;ConfusionMatrixDisplay() 和 imshow()&#xff0c;以及两种好看的co…

分布式cap理论学习

【分布式】CAP理论详解 一致性(Consistency) 代表数据在任何时刻&#xff0c;任何分布式节点&#xff0c;看到的都是符合预期的。有点类似于幂等&#xff0c;无论访问哪个节点&#xff0c;得到结果数据一致。 可用性(Availability) 强调的是任意时刻一定能读到数据&#xff…

自动驾驶系列—从数据采集到存储:解密自动驾驶传感器数据采集盒子的关键技术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

Build event

libevent的基本操作单元是事件。每个事件代表一组条件的集合&#xff0c;这些条件包括&#xff1a; 文件描述符已经就绪&#xff0c;可以读取或者写入 文件描述符变为就绪状态&#xff0c;可以读取或者写入&#xff08;仅对于边沿触发IO&#xff09; 超时事件 发生某信号 用…

flink cdc 应用

SQLServer 1. The db history topic or its content is fully or partially missing. Please check database history topic configuration and re-execute the snapshot. 遇到了一下问题&#xff0c;多次尝试&#xff0c;最终发现是数据库大小写要一致。 Caused by: io.deb…

centos7安装教程

以下是CentOS 7详细的安装教程: 一、准备工作 下载CentOS 7镜像文件 可以从CentOS官方网站(https://www.centos.org/download/)下载适合你硬件架构(如x86_64)的ISO镜像文件。准备USB或光盘 如果使用USB安装,需要一个容量不小于2GB的可引导USB。可以使用工具如Rufus(适用…