1. macrotask (宏任务)和 microtask (微任务)
这两个是指两个队列,脚本整体代码、setTimeout、setInterval、setImmediate、I/O、的回调及UI渲染都会被加入到 macrotask 队列中, process.nextTick回调、Promise(浏览器实现)、MutationObserver回调都会被加入到microtask队列。
- 1 当浏览器进入事件循环时,先去看macrotask中有没有具备执行条件的任务
- 2 如果有则选取一个(只选一个)执行
- 3 没有则去看microtask队列有没有具备执行条件的任务
- 4 有则全部执行(全部执行)
- 5 去更新页面
- 6 然后重复以上行为
2. Promise 介绍
Promise主要的功能是将异步执行结果保存起来,从而使得我们不再用回调的方式去处理异步任务的结果,我们通过一个回调将异步任务的结果保存起来,在以后某个时刻通过各种姿势去使用它new romise(excutor).then(function(result)}).then(function(result){});的方式去执行异步任务,Promise 是通过回调来实现的,但它将回调统一,使得我们不在去直接接触回调,用看似有点像同步的方式去写异步任务,也就仅仅是这样而已,并没有什么新东西,这种同步方式只能说是类似同步,下一篇会讲写法更接近同步、更加有建术的 generator 。
3. Promise 的构造函数应该是这样的
function Promise(excutor) {
var self = this;
var status = 'PENDING'; //Promise的当前状态
var data = undefined; //当前excutor(就是用户的异步任务)的执行结果
self.onResolvedCallback = []; //excutor 执行成功后回调函数队列
self.onRejectedCallback = []; //excutor 执行失败后回调函数队列
function resolve(value) {
// TUDO 异步任务执行成功后的行为
// 1 改变当前promise的status值 调用该方法 PENDING->RESOLVED
// 2 执行onResolvedCallback队列里面的所有回调函数并传递异步任务的异步处理结果
// onResolvedCallback[i](data)
}
function reject(value) {
// TUDO 异步任务执行失败后的行为
// 1 改变当前promise的status值 调用该方法 PENDING->REJECTED
// 2 执行onRejectedCallback队列里面的所有回调函数并传递异步任务的异步处理结果
// onRejectedCallback[i](data)
}
excutor(resolve, reject); //执行用户添加的任务
}
Promise.prototype.then = function (onResolved, onRejected) {
// onResolved异步任务执行成功的回调
// onRejected异步任务执行失败后的回调
}
看起来就是这样的 具体使用方式
new Promise(function(resolve,reject){
setTimeout(function(result){
// resolve(result) 或reject(result) 根据返回值决定
//这里我们确实还是写了回调,但是我们在回调里面没有做太多的事件
//仅仅是传递了异步处理结果 想想如果,我们在这里又要做依赖于result的
//异步任务会怎样,回调地狱,代码结构混乱,不容易读
});
}).then(function(result){}) //这里的result是从上面的那个回调里面传递的,是同一个值
4. 具体实现
(1) resolve 函数和 reject 函数
function Promise(excutor) {
var self = this;
var status = 'PENDING'; //Promise的当前状态
var data = undefined; //当前excutor(就是用户的异步任务)的执行结果
self.onResolvedCallback = []; //excutor 执行成功后回调函数队列
self.onRejectedCallback = []; //excutor 执行失败后回调函数队列
function resolve(value) {
// TUDO 异步任务执行成功后的行为
if(self.status === 'PENDING') {
self.status = 'REJECTED'; // 改变当前promise的status值 调用该方法 PENDING->RESOLVED
self.data = value; // 更新当前的data
for(var i in self.onResolvedCallback) {
self.onResolvedCallback[i](self.data); //执行onResolvedCallback队列里面的所有回调函数并传递异步任务的异步处理结果
}
}
}
function reject(reson) {
// TUDO 异步任务执行失败后的行为
if(self.status === 'PENDING') {
self.status = 'REJECTED';
self.data = reson;
for(var i in self.onRejectedCallback) {
self.onRejectedCallback[i](self.data);
}
}
}
try {
excutor(resolve.bind(this), reject.bind(this)); //执行用户添加的任务
/*这里为什么要通过通过 bind 来绑定上下文呢,主要是 resolve 依赖当
前 promise的内部属性,在excutor函数体内,我们是通过 resolve(data)
或者reject(reson) 的方式来调用通过这种方式调用 在非严格模式
resolve或reject的上下文是windows,在严格模式下是undefined*/
} catch(e) {
reject.bind(this)(e); //出现异常则通过reject向后传递
}
}
(2) then 方法的实现
then方法应当具备如下功能
(1) then方法必须返回一个Promise对象,也就是,对应用户添加的逻辑要进行包装
(2) 同一个对象可多次调用then来添加逻辑,并且会按照添加顺序执行
//例如:
var promise = new Promise(function(resolve, reject) {
//异步操作 并且在回调中调用了 resolve或者 reject将异步处理结果传递出去
});
promise.then(function(data){
//对异步结果data进行操作
});
promise.then(function(data){
//对异步结果data进行操作
});
//promise是同一个对象
(3) 可以链式调用
//例如:
new Promise(function(resolve, reject){
//异步操作 并且在回调中调用了 resolve或者 reject将异步处理结果传递出去
}).then(function(data){}).then(function(data){});
这里第一次和第二次调用then的对象是两个不同的对象,这里要注意
[1] 如果第一个then的参数也是一个Promise那么在第二个then的中会得到第一个then的处理结果
[2] 如果第一个then的参数是一个函数并且有返回值,则在第二个then中会接收到这个返回值
[3] 其它情况下都会得到通过 new Promise(fn) 这里这个异步fn的处理结果
(4) 我们写的Promise 要和所有符合Promise规范的 Promise 能协同工作(例如浏览器自带的、第三方库的)
/**
*@parms onResolved 任务成功时执行的回调
*@parms onRejected 任务失败时执行的回调
*/
Promise.prototype.then = function(onResolved, onRejected) {
var self = this;
var promiseThen; //then方法返回的promise
//如果 onResolved或onRejected 不是函数我们忽略它,并且添加一个用于传递异步结果得函数
onResolved = typeof onResolved ==='function' ? onResolved : function(value) {resolve(value);}
onRejected = typeof onRejected ==='function' ? onRejected : function(reson) {resolve(reson);}
/*如果在调用then添加依赖于异步处理结果的时候,异步任务已经执行完了,那么
用异步的结果执行then里面添加的任务*/
if(self.status === 'RESOLVED') {
//这里要将then里面要添加的任务包装成一个Promise,返回Promise 是为了(3)中的链式调用
return promiseThen = new Promise(function(resolve, reject) {
try {
//执行then添加的任务
var x = onResolved(self.data);
if(x instanceof Promise) {
/*如果then添加的任务有返回值,并且返回值是Promise对象
则让promiseThen去接受x的状态和data值注意,这里将
promiseThen的resolve,和reject作为x的then参数传入,这
样当promiseThen的resolve和reject会在适当的时候被调用
从而使得promiseThen接受了x的状态和data值Promise的不
同对象之间能实现相互影响,是通过将自己的resolve和
reject添加到其它对象中*/
x.then(resolve, reject);
}
else resolve(x); //如果x不是Promise使用x的值作为promiseThen的结果
} catch (e) {
reject(e);
}
});
}
//和上面逻辑相同
if(self.status === 'REJECTED') {
return promiseThen = new Promise(function(resolve, reject){
try {
var x = onRejected(self.data);
if(x instanceof Promise){
x.then(resolve, reject);
}
}catch(e) {
reject(e);
}
});
}
if(self.status === 'PENDING') {
//当前Promise的异步任务没有执行完,则将then里面的异步任务 包装后放入队列
//包装方法和上面一样,区别是上面立即执行,这里放入回调队列。
return promiseThne = new Promise(function() {
self.onResolvedCallback.push(function(){
try {
var x = onResolved(self.data);
if(x instanceof Promise) {
x.then(resolve, reject);
} catch(e) {
reject(e);
}
}
});
self.onRejectedCallback.push(function(){
try {
var x = onRejected(self.data);
if(x instanceof Promise) {
x.then(resolve, reject);
} catch(e) {
reject(e);
}
}
});
});
}
}
写到这里Promise具体实现的 (1)、(2)、(3)的功能已经完成了,也就是如果不考虑与其它
的Promise交互那么Promise的原理已经说清楚了,本来打算一篇文章写完,但是篇幅太
长,所以打算分两篇文章来写,下一篇是generator,之后我会按照规范将(4)部分实现。