关于Promise,在这一篇里总结完全!

226 阅读4分钟

Promise的使用

  1. promise最简单的使用方式:
const p1 = new Promise((resolve,reject) => {
	console.log('创建一个Promise')
	resolve('成功了')   
    //这里假设操作成功,
    //resolve中的参数将作为.then的参数(成功的回调)
})

	console.log('after new Promise')

const p2 = p1.then(
	(data) => {
    	console.log(data)
        throw new Error('失败了')
    }     //因为前面写的resolve,所以这里没写(err)=> {}
)

const p3 = p2.then(
	(data) => {
    	console.log('success',data)
    },
    (err) => {
    	console.log('failed',err)   //因为上一步抛出了错误,所以这里打印Error的信息
    }
)


//控制台打印:
创建一个Promise
after new Promise
成功了
failed Error:失败了
  • 默认情况下是等待状态pending,如果有一天状态转变为成功就成功了,如果状态变成失败就失败了,状态一旦改变了就不能再改变了。同时调用 resolve 函数和 reject 函数,默认会采取第一次调用的结果

  • 上一个then不管走成功回调还是失败回调,只要返回一个普通值(非抛出错误,非返回Promise),就会走下一个的成功回调(参数是undefined),如果返回Promise,则由Promise来修改下一次的状态,从而觉得执行哪一个回调,成功or失败。

  • 注意上面的executor函数体里写的是同步操作,如果是异步操作,.then时,Promise的状态仍是pedding

  • 当执行.then时状态仍为pedding时,这时要把成功和失败的回调分别存在各自的队列里,等待请求结果回来后,在resolve或reject中执行(发布订阅)

  • 手写实现的Promise里,使用 setTimeout 实现 promise 的异步。

  • 由于原生的 Promise 是V8引擎提供的微任务,我们无法还原V8引擎的实现,所以这里使用 setTimeout 模拟异步,所以原生的是微任务,这里是宏任务。 @知乎 齐小神

手写promise


const resolvePromise = (promise2, x, resolve, reject) => {
  // 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise  Promise/A+ 2.3.1
  if (promise2 === x) { 
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // Promise/A+ 2.3.3.3.3 只能调用一次
  let called;
  // 后续的条件要严格判断 保证代码能和别的库一起使用
  if ((typeof x === 'object' && x != null) || typeof x === 'function') { 
    try {
      // 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候)  Promise/A+ 2.3.3.1
      let then = x.then;
      if (typeof then === 'function') { 
        // 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty  Promise/A+ 2.3.3.3
        then.call(x, y => { // 根据 promise 的状态决定是成功还是失败
          if (called) return;
          called = true;
          // 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
          resolvePromise(promise2, y, resolve, reject); 
        }, r => {
          // 只要失败就失败 Promise/A+ 2.3.3.3.2
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4
        resolve(x);
      }
    } catch (e) {
      // Promise/A+ 2.3.3.2
      if (called) return;
      called = true;
      reject(e)
    }
  } else {
    // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4  
    resolve(x)
  }
}

class myPromise {
	constructor(executor){
		this.state = 'pedding'
		this.value = undefined
		this.reason = undefined

		this.onFullfilledList = []
		this.onRejectedList = []


		let resolve = (value) => {
			if(this.state === 'pedding'){
				this.state = 'success'
				this.value = value

				this.onFullfilledList.forEach(fn => fn())
			}
		}

		let reject = (reason) => {
			if(this.state === 'pedding'){
				this.state = 'failed'
				this.reason = reason

				this.onRejectedList.forEach(fn => fn())
			}
		}

		try {
			executor(resolve,reject)
		}catch(error){
			reject(error)
		}
	}


	then(onFullfilled,onRejected) {
		onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : v => v
		onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}

		let promise2 = new Promise((resolve,reject) => {
			if(this.state === 'success'){
				setTimeout(()=>{
					try {
						let x = onFullfilled(this.value)

						resolvePromise(promise2,x,resolve,reject)
					}catch(err){
						reject(err)
					}
				},0)
			}

			if(this.state === 'failed'){
				setTimeout(()=>{
					try{
						let x = onRejected(this.reason)

						resolvePromise(promise2,x,resolve,reject)
					}catch(err){
						reject(err)
					}
				},0)
			}

				if(this.state === 'pedding'){
					this.onFullfilledList.push(()=>{
						setTimeout(()=>{
							try {
								let x = onFullfilled(this.value)

								resolvePromise(promise2,x,resolve,reject)
							}catch(err){
								reject(err)
							}
						},0)
					})

					this.onRejectedList.push(()=>{
					setTimeout(()=>{
						try{
							let x = onRejected(this.reason)

							resolvePromise(promise2,x,resolve,reject)
						}catch(err){
							reject(err)
						}
					},0)
				})
			}

			})
		return promise2
	}	
}

const resolvePromise = function(promise2,x,resolve,reject){
}

Promise Api

Promise.resolve() 返回一个成功的promise Promise.reject() 返回一个失败的promise Promise.all(arr) 都成功才成功,有一个失败则失败 Promise.race(arr) 取最快返回的结果的状态 Promise.prototype.catch((e)=>{}) 捕获错误,如果没有自己的失败处理函数,走catch

Promise.all

  • 只有参数内所有的promise返回resolve,才会返回resolve
  • 参数中有一个失败rejected,就返回rejected.原因是第一个失败rejected的结果


Promise.all1 = function(arr){
	if(!Array.isArray(arr)){
		return new TypeError('err')
	}


	return new Promise((resolve,reject)=>{
		let resArr = []
		let count = 0
		const processRes = (value,index) => {
			resArr[index] = value
			if(++count === arr.length){
				resolve(resArr)
			}
		}

		for(let i = 0; i < arr.length; i++){
			if(arr[i] && typeof arr[i].then === 'function'){
				arr[i].then((value)=>{
					processRes(value,i)
				},reject)
			}else{
				processRes(arr[i],i)
			}
		}
	})

}

Promise.race

Promise.race = function(arr){
	return new Promise((resolve,reject)=>{
		for(let i = 0; i < arr.length; i++){
			if(arr[i] && typeof arr[i].then === 'function'){
				arr[i].then((value)=>{
					resolve(value)
				})
			}else{
				resolve(arr[i])
			}
		}
	})
}
//这么简单得益于promise的状态只能改变一次,即resolve和reject都只被能执行一次

常见的问题:

  1. Promise解决了什么问题?(当然是回调地狱啦)
  2. Promise的实现有哪些?
  3. 常见的Api有哪些?
  4. 手写Promise?
  5. Promise在事件循环的执行过程是怎样的?
  6. Promise有什么缺陷,如何解决?

Q&A_1:Promise解决了什么问题?

  • 异步请求的嵌套,后面的函数依赖前面函数的返回结果
  • 多个异步请求并发,开发时往往需要同步请求最终的结果

解决:

  • Promise链式调用解决回调地狱
  • Promise.all获取多个任务的错误处理

new Promise()接受一个executor函数作为参数,在函数体里写要进行的请求,此函数会立即执行,并且拿到请求结果,修改Pedding状态,请求成功就改为成功状态,失败就为失败状态,然后用.then方法,拿到返回结果(成功的data和失败的err)

promise错误处理 Promise.reject(),直接将值变成错误结果 promise.prototype.catch,用来捕获promise异常,相当于一个没有成功的then promise。prototyoe.finally,finally表示无论如何都会执行,如果返回失败,会将其传到catch中