手写promise(二)

onFulfilled 和 onRejected 必须作为函数调用
then() 也许会在同一个promise中调用多次
 - 当promise变为完成态,所有相关的onFulfilled回调都被执行,且是按照回调函数注册的顺序执行
 - 当promise变为拒绝态,所有相关的onRejected回调都按注册顺序执行
then必须返回一个promise: promise2 = promise1.then(onFulfilled, onRejected)
 - 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒绝原因 reason
 - 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
 - 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的拒绝原因
 - 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)

Promise 解决过程 是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise

运行 [[Resolve]](promise, x) 需遵循以下步骤:
x 与 promise 相等
如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise

x 为 Promise
如果 x 为 Promise ,则使 promise 接受 x 的状态 注4:
	如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
	如果 x 处于执行态,用相同的值执行 promise
	如果 x 处于拒绝态,用相同的据因拒绝 promise
	
x 为对象或函数
如果 x 为对象或者函数:
把 x.then 赋值给 then 
如果取 x.then 的值时抛出错误 e ,则以 e 为拒绝原因拒绝 promise
如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
如果调用 then 方法抛出了异常 e:
如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
否则以 e 为据因拒绝 promise
如果 then 不是函数,以 x 为参数执行 promise
如果 x 不为对象或者函数,以 x 为参数执行 promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise 


------------------------------------------------------------------------
promise原理
观察者模式 + 事件循环 = 异步模式
观察者模式的核心:订阅和发布
什么时候订阅? 事件循环遍历微任务的时侯订阅
什么时候发布? 当满足一定条件时发布。这个由开发者来控制 而且promise的实现中保证了发布晚于订阅
new Promise(function(resolve, reject) {
	resolve(1)
}).then(value => console.log(value))
比如这个例子,如果 resolve()里面没有一个保证(比如 setTimeout()) 那么将then里面的回调将不会得到通
知 resolve()就是用来通知回调函数的
如请求数据,当请求完后,resolve()一下【宏任务】 then()时注册的回调即被调用  
前面说过微任务的遍历在宏任务的遍历之前


函数调用栈与任务队列
Javascript有一个main thread 主进程和call-stack (一个调用堆栈),在对一个调用堆栈中的task处理的时候,其他的都要等着。当在执行过程中遇到一些类似于setTimeout等异步操作的时候,会交给浏览器的其他模块 (以webkit为例,是webcore模块) 进行处理,当到达setTimeout指定的延时执行的时间之后,task (回调函数)会放入到任务队列之中。一般不同的异步任务的回调函数会放入不同的任务队列之中。等到调用栈中所有task执行完毕之后,接着去执行任务队列之中的task (回调函数)。

调用栈中遇到DOM操作、ajax请求以及setTimeout等WebAPIs的时候就会交给浏览器内核的其他模块进行处理,webkit内核在Javasctipt执行引擎之外,有一个重要的模块是webcore模块。webcore分别提供了DOM Binding、network、timer模块来处理底层实现。等到这些模块处理完这些操作的时候将回调函数放入任务队列中,之后等栈中的task执行完之后再去执行任务队列之中的回调函数。

当遇到setTimeout的时候,执行引擎将其添加到栈中。但发现这是异步事件,立刻弹出,让相应模块处理,处理完后,去对应的任务队列注册。【注意,这里什么时候注册回调是不确定的,比如setTimeout(()=> {console.log(1), 10000})10秒后 相应模块处理完后,去宏任务队列注册回调


任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。
macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

Promise构造函数中的第一个参数,是在new的时候执行,因此不会进入任何其他的队列,而是直接在当前任务直接执行了,而后续的.then则会被分发到micro-task的Promise队列中去。


----------------------------------------------------------------

function myPromise(exec) {
	this.onfulfilled_cb = []
	var resolve = function() {
		setTimeout(() => {
			// 改变promise状态
			...
			// 获取终值
			...
			// 执行回调
			this.onfulfilled_cb.forEach((cb, i) => {
				cb(终值)
			})
		})
	}
	var reject = function() {...}
	...
	try {
		exec(resolve, reject)
	}catch (e){
		reject(e)
	}
}
// 微任务里面注册回调
myPromise.prototype.then(onFulfilled, onRejected) {
	return new myPromise((resolve, reject) => {
		// 注册成功回调
		this.onfulfilled_cb.push((value) => {
			try{
				onFulfilled(value)
			}
		})
		// 注册失败回调
		...
	})
}
示例分析
new myPromise(function(resolve, reject) {
	resolve(1)
}).then(value => console.log(value))
上面代码中的 resolve()里面的内容在setTimeout里面 交给js引擎外的webcore模块 当webcore模块到指定时间后(如果没有指定时间,将取决于其他消息),将会去宏任务队列注册。
遇到 then() 方法进入了微任务队列 【如果微任务很久都没执行完,也就是很久才去微任务队列注册,而假设此时宏任务已经完成了注册,事件循环发现微任务队列还没有任务,事件循环发现宏任务队列有任务,拿出执行栈执行,但这不可能】实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。



function myPromise(constructor){
 let self=this;
 self.status="pending" //定义状态改变前的初始状态
 self.value=undefined;//定义状态为resolved的时候的状态
 self.reason=undefined;//定义状态为rejected的时候的状态
 function resolve(value){
  //两个==="pending",保证了状态的改变是不可逆的
  if(self.status==="pending"){
   self.value=value;
   self.status="resolved";
  }
 }
 function reject(reason){
  //两个==="pending",保证了状态的改变是不可逆的
  if(self.status==="pending"){
   self.reason=reason;
   self.status="rejected";
  }
 }
 //捕获构造异常
 try{
  constructor(resolve,reject);
 }catch(e){
  reject(e);
 }
}

myPromise.prototype.then=function(onFullfilled,onRejected){
 let self=this;
 switch(self.status){
  case "resolved":
  	onFullfilled(self.value);
  	break;
  case "rejected":
  	onRejected(self.reason);
  	break;
  default: 
  	break; 
 }
}

var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1
然鹅一旦修改下
var p=new myPromise(function(resolve,reject){setTimeout(() => {resolve(1)})});
p.then(function(x){console.log(x)})
这种情况就不行了
因为resolve没有及时发出信号。导致then注册的回调无法执行


// promise 采用了观察者模式,用特定方式 注册 对应 状态 的事件处理函数
// 
// 定义promise 状态
const pending = "pending"
const fulfilled = "fulfilled"
const rejected = "rejected"
// promise 是一个对象或函数 它有一个方法then()
function myPromise(fn) {
	if(typeof fn != "function") return 
	
	this._state = pending  // 初始状态为等待态
	this._value = undefined  // 保存执行态的value 
	this._reason = undefined  // 拒绝态的reason
	this.onfulfilled_cb = []  // 多次调用 有多个onFulfilled
	this.onrejected_cb = []  // 多次调用 有多个onRejected
	
	// 处于等待态时,promise 需满足以下条件:可以迁移至执行态或拒绝态
	// promise成功时的操作 如改变状态 回调执行  promise处于执行态时 有一个不可变的终值
	this.resolve = function(value) {
		// value 是成功时接收到的终值
		// 从pending 变为 fulfilled 
		// 处于执行态时不能迁移至其他任何状态 处于拒绝态时不能迁移至其他任何状态
		// 保证在then() 成功执行后的那轮事件循环之后的新的执行栈中执行
		// 调用 resolve() 但这个是宏任务 进入宏任务队列
		// 然后.then() 进入微任务队列  同步代码执行完毕 先去微任务队列查看
		// 在微任务中添加回调至回调数组  微任务完毕 再去查看宏任务队列
		// 遍历回调数组 调用微任务队列时添加的回调  
		// 如此一来实现了观察者模式  利用了事件循环的原理完成了异步编程
		setTimeout(() => {
			if(this._state != pending) return
			this._state = fulfilled
			this._value = value 
			this.onfulfilled_cb.forEach((cb, i) => {
				cb(this._value)
			})
		})
	}
	// 
	this.reject = function(reason) {
		setTimeout(() => {
			if(this._state === "pending") {
				this._state = rejected
				this._reason = reason
				this.onrejected_cb.forEach((cb, i) => {
					cb(reason)
				})
			} 
		})
	}
	
	// promise 不会处理任务  它只是协调
	
	// then() 方法属于微任务  就像setTimeout属于宏任务 都有相应模块处理
	this.then = function(onFulfilled, onRejected) {
		// 如果onFulfilled不是函数 必须被忽略
		if(typeof onFulfilled != "function") return 
		// 如果 onRejected 不是函数,其必须被忽略
		if(typeof onRejected != "function") return 
		
		let newPromise
		
		// 刚进来一开始肯定是pending 
		// promise的原理是观察者模式 在then()方法中收集回调
		// 当resolve时 或 reject时 调用刚才收集的回调
		if(this._state === pending) {
			// then 方法必须返回一个 promise 对象
			return newPromise = new myPromise((resolve, reject) => {
				// 在新的promise中的fn先暂存回调
				this.onfulfilled_cb.push((value) => {
					// 将onFulfilled保存到数组
					try{
			// 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程
				// Promise 解决过程 是一个抽象的操作,其需输入一个 promise 和一个值
						let x = onFulfilled(value)
						// promise解决过程封装
						// 闭包 newPromise
						resolvePromise(newPromise, x, resolve, reject)
					} catch(e) {
						reject(e)
					}
				})
				this.onrejected_cb.push((reason) => {
					try {
						let x = onRejected(reason)
						resolvePromise(newPromise, x, resolve, reject)
					} catch(e) {
						reject(e)
					}
				})
			})
		}
		// 如果是同一个promise重复调用then() 重复调用意味着 _state 是 fulfilled 或 rejected
		if(this._state === fulfilled) {
			return newPromise = new myPromise((resolve, reject) => {
				// 为了确保回调函数在then()之后执行
				setTimeout(() => {
					try {
						let x = onFulfilled(value)
						resolvePromise(newPromise, x, resolve, reject)
					}catch (e) {
						reject(e)
					}
				})
			})
		}
		
		
	}
	
	try 
		fn(resolve, reject)
	} catch(e) {
		reject(e)
	}
}
// 递归实现then的链式调用
function resolvePromise(newPromise, x, resolve, reject) {
	// 1. x 和 promise 指向同一对象 以 TypeError 为拒绝原因拒绝执行 promise 
	if(x === newPromise) return reject(new TypeError('循环引用'))
	let called = false  // 记录是否已经调用  promise的状态只能更改一次
	// 2. x 为 promise  则使promise接收x的状态
	if(x instanceof myPromise) {
		// 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
		if(x._state == pending) {
			x.then(y => {
				resolvePromise(newPromise, y, resolve, reject)
			}, reason => {
				reject(reason)
			})
		}
		// 如果 x 处于执行态,用相同的值执行 promise
		else {
			x.then(resolve, reject)
		}
	}else if(x != null && typeof x === "object" || typeof x === "function") {
		// 如果 x 为对象或者函数
		try {
			let then;
			// 把 x.then 赋值给 then
			then = x.then;
			if(typeof then === "function") {
				// 如果 then 是函数,将 x 作为函数的作用域 this 调用之
				// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,
				// 第二个参数叫做 rejectPromise:
	// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,
	// 则优先采用首次调用并忽略剩下的调用  使用called
				then.call(x, y => {
	//  如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
					if(called) return
					called = true
					resolvePromise(newPromise, y, resolve, reject)
				}, reason => {
	//  如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
					if(called) return 
					called = true
					reject(reason)
				})
			}else {
				// 如果 then 不是函数,以 x 为参数执行 promise
				resolve(x)
			}
		}
		catch(e) {
			// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
			if(called) return 
			called = true
			return reject(e)
		}
	}else {
		// 如果 x 不为对象或者函数,以 x 为参数执行 promise
		resolve(x)
	}
}
























你可能感兴趣的:(web前端,javascript,前端,css3)