异步处理 —— Promise原理(一)

目标:

  • 了解Promise特点
  • 掌握Promise的定义和用法
  • 通过手写Promise进一步加深理解

1、Promise的基本功能

基本写法及定义:

let P1 = new Promise((resolve, reject) => {
	resolve('success')
	rejected('fail')
})
console.log('P1', P1)

let P2 = new Promise((resolve, reject) => {
	reject('fail')
	resolve('success')
})
console.log('P2', P2)

let P3 = new Promise((resolve, reject) => {
	throw('error')
})
console.log('P3', P3)

执行上面的代码之后执行结果如下所示:
在这里插入图片描述
首先先了解Promise最基本的几个特点:

  • Promise 有三个状态,分别是 pendingfulfilledrejected
    • 执行了 resolve 之后, Promise 的状态变为 fulfilled
    • 执行了 rejecte 之后, Promise 的状态变为 rejected
    • 在执行上面两个方法之前, Promise 的状态为 pending
  • Promise 的状态一旦改变之后就不可再变
  • 遇到 throw 会抛出异常,相当于执行了 reject

现在来手写下具备这些特点的 Promise

class MyPromise {
	constructor(exector) {
		this.initValue() //初始化要用到的参数
		this.initBind() //改变传入的resolve和reject的指针指向
		// 通过try和catch实现遇到throw的时候执行reject方法的功能
		try {
			exector(this.resolve, this.reject)
		} catch(err) {
			this.reject(err)
		}
	}
	initValue() {
		this.PromiseState = 'pending'
		this.PromiseResult = null
	}
	initBind() {
		this.resolve = this.resolve.bind(this)
		this.reject = this.reject.bind(this)
	}
	resolve(result) {
		if(this.PromiseState != 'pending') return //保证状态不可逆
		this.PromiseState = 'fulfilled'
		this.PromiseResult = result
	}
	reject(reason) {
		if(this.PromiseState != 'pending') return //保证状态不可逆
		this.PromiseState = 'rejected'
		this.PromiseResult = reason
	}
}

2、then

我们平时在业务中用 Promise 用到的最多的就是 then 方法了,它的用法如下:

// 马上输出 ”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. resolvereject 在定时器里,则定时器结束后再执行 then
  4. then 支持链式调用,下一次 then 执行受上一次 then 返回值的影响;

2.1、先来实现then方法

class MyPromise {
	constructor(exector) {
		this.initValue() 
		this.initBind() 

		try {
			exector(this.resolve, this.reject)
		} catch(err) {
			this.reject(err)
		}
	}
	initValue() {
		this.PromiseState = 'pending'
		this.PromiseResult = null
	}
	initBind() {
		this.resolve = this.resolve.bind(this)
		this.reject = this.reject.bind(this)
	}
	resolve(result) {
		if(this.PromiseState != 'pending') return
		this.PromiseState = 'fulfilled'
		this.PromiseResult = result
	}
	reject(reason) {
		if(this.PromiseState != 'pending') return 
		this.PromiseState = 'rejected'
		this.PromiseResult = reason
	}
	then(onFulfilled, onRejected) {
	    // 判断参数类型,保证回调是函数类型
		onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val //成功回调是值的时候直接返回该值,供下一次链式调用时使用
		onRejected = typeof onRejected == 'function' ? onRejected : reason => { throw reason } //失败回调传值的时候抛出错误为该值的异常
		
		// 对应的状态下执行对应的回调方法
		if(this.PromiseState == 'fulfilled') onFulfilled(this.PromiseResult)
		if(this.PromiseState == 'rejected') onRejected(this.PromiseResult)		
	}
}

但是这段代码只是实现了前两个的的基本特点, timeout 和链式调用是不可用的。

2.2、timeout模拟异步情况

关键问题是解决如何在一秒后再执行 then 里面的回调。我们可以把 then 里面的两个回调保存起来,等执行完 resolve 或者 reject 之后再去判断状态决定执行哪个回调。回调通过数组来保存,以解决后续的链式调用的问题。
在这里插入图片描述

class MyPromise {
	constructor(exector) {
		this.initValue()
		this.initBind()
		try {
			exector(this.resolve, this.reject)
		} catch(err) {
			this.reject(err)
		}
	}
	initValue() {
		this.PromiseState = 'pending'
		this.PromiseRsult = null
		this.fulfilledCallbacks = []
		this.rejectedCallbacks = []
	}
	initBind() {
		this.resolve = this.resolve.bind(this)
		this.reject = this.reject.bind(this)
	}
	resolve(result) {
		if(this.PromiseState != 'pending') return
		this.PromiseState = 'fulfilled'
		this.PromiseResult = result
		while(this.fulfilledCallbacks.length) {
			const x = this.fulfilledCallbacks.shift()
			x(this.PromiseResult)
		}
		
	}
	reject(reason) {
		if(this.PromiseState != 'pending') return
		this.PromiseState = 'rejected'
		this.PromiseResult = reason
		while(this.rejectedCallbacks.length) {
			const x = this.rejectedCallbacks.shift()
			x(this.PromiseResult)
		}
	}
	then(onFulfilled, onRejected) {
		onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
		onRejected = 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.fulfilledCallbacks.push(onFulfilled.bind(this)) //需要将传入的回调绑定到该实例上,原理同initBind()里的bind
			this.rejectedCallbacks.push(onRejected.bind(this)) //同上
		}
	}
}

2.3、链式调用

then 支持链式调用,下一次 then 执行受上一次 then 返回值的影响,如:

// 链式调用 输出 200
const p1 = 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 p2 = 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))

可以得出链式调用有以下特点:

  • then 方法本身会返回一个新的 Promise 对象
  • 如果返回值非 Promise 对象,新 Promise 对象就是成功,值为此返回值
  • 如果返回值是 Promise 对象,新 Promise 对象的结果跟着返回值的 Promise 的结果走
class MyPromise {
	constructor(exector) {
		this.initValue()
		this.initBind()
		try {
			exector(this.resolve, this.reject)
		} catch(err) {
			this.reject(err)
		}
	}
	initValue() {
		this.PromiseState = 'pending'
		this.PromiseRsult = null
		this.fulfilledCallbacks = []
		this.rejectedCallbacks = []
	}
	initBind() {
		this.resolve = this.resolve.bind(this)
		this.reject = this.reject.bind(this)
	}
	resolve(result) {
		if(this.PromiseState != 'pending') return
		this.PromiseState = 'fulfilled'
		this.PromiseResult = result
		while(this.fulfilledCallbacks.length) {
			this.fulfilledCallbacks.shift()(this.PromiseResult)
		}
		
	}
	reject(reason) {
		if(this.PromiseState != 'pending') return
		this.PromiseState = 'rejected'
		this.PromiseResult = reason
		while(this.rejectedCallbacks.length) {
			this.rejectedCallbacks.shift()(this.PromiseResult)
		}
	}
	then(onFulfilled, onRejected) {
		onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : val => val
		onRejected = 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) {
						x.then(resolve, reject)
					} else {
						resolve(x)
					}
				} catch(err) {
					reject(err)
					throw(err)
				}
			}

			if(this.PromiseState == 'fulfilled') resolvePromise(onFulfilled)
			else if(this.PromiseState == 'rejected') resolvePromise(onRejected)
			else if(this.PromiseState == 'pending') {
				this.fulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled)) 
				this.rejectedCallbacks.push(resolvePromise.bind(this, onRejected))
			}
		})
		return thenPromise
	}
}

3、补充知识点

3.1、setTimeout

setTimeout 是单线程,类似异步,但不是异步

console.log(1)
setTimeout(() => {
    console.log(2);
}, 2000)
console.log(3)

setTimeout(() => {
    console.log(4)
})
setTimeout(() => {
    console.log(5);
}, 0)
setTimeout(() => {
    console.log(6)
})
console.log(7)
setTimeout(() => {
    console.log(8);
}, 1000)

/*
	控制台依次输出以下结果
	1
	3
	7
	4
	5
	6
	8
	2
*/

可以看出,setTimeout 并没有阻塞主线程,因此 setTimeout 明显不是同步执行。但是虽然设置了第二个 setTimeout 的时间为0,但是仍是在输出1、3、7后执行的。所以也明显不是异步。

首先,要先清楚JavaScript中的任务队列这一概念。在JavaScript中,任务分为两种:异步任务和同步任务。

  • 同步任务:在主线程上排队执行的任务,只有在前一个任务执行完毕后,才能继续执行下一个任务
  • 异步任务:不进入主线程,而是进入任务队列的的任务。等主线程的任务全部执行完毕之后,主线程会通过event loop(事件循环)去询问任务队列中是否有可执行的任务,有则进入主线程执行。

setTimeout 就是被放入异步任务队列中,等待主线程的任务执行完毕后再到主线程中执行。因此 setTimeout 的异步只是假象。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值