1.promise常用方法

Promise.all()
Promise.race()
Promise.resolve()
Promise.reject()
Promise.prototype.catch()
Promise.prototype.finally()
Promise.prototype.then()
2.考虑下面一种获取用户id的请求处理
//例1
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(results) {
resolve(results.id)
})
})
}

getUserId().then(function(id) {
//一些处理
})
getUserId方法返回一个promise,可以通过它的then方法注册(注意注册这个词)在promise异步操作成功时执行的回调。这种执行方式,使得异步调用变得十分顺手。

原理剖析

那么类似这种功能的​​Promise​​怎么实现呢?其实按照上面一句话,实现一个最基础的雏形还是很easy的。

this.then = function (onFulfilled) {
callbacks.push(onFulfilled);
return this;
};
getUserId().then(function (id) {
// 一些处理
}).then(function (id) {
// 一些处理
});

promise底层实现_异步操作

 

 

function Promise(fn) {
var state = 'pending',
value = null,
callbacks = [];

this.then = function (onFulfilled) {
if (state === 'pending') {
callbacks.push(onFulfilled);
return this;
}
onFulfilled(value);
return this;
};

function resolve(newValue) {
value = newValue;
state = 'fulfilled';
setTimeout(function () {
callbacks.forEach(function (callback) {
callback(value);
});
}, 0);
}

fn(resolve);
}
function Promise(fn) {
var state = 'pending',
value = null,
callbacks = [];

this.then = function (onFulfilled) {
return new Promise(function (resolve) {
handle({
onFulfilled: onFulfilled || null,
resolve: resolve
});
});
};

function handle(callback) {
if (state === 'pending') {
callbacks.push(callback);
return;
}
//如果then中没有传递任何东西
if(!callback.onFulfilled) {
callback.resolve(value);
return;
}

var ret = callback.onFulfilled(value);
callback.resolve(ret);
}


function resolve(newValue) {
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve);
return;
}
}
state = 'fulfilled';
value = newValue;
setTimeout(function () {
callbacks.forEach(function (callback) {
handle(callback);
});
}, 0);
}

fn(resolve);
}
getUserId()
.then(getUserJobById)
.then(function (job) {
// 对job的处理
});

function getUserJobById(id) {
return new Promise(function (resolve) {
http.get(baseUrl + id, function(job) {
resolve(job);
});
});
}

  1. ​then​​​方法中,创建并返回了新的​​Promise​​​实例,这是串行​​Promise​​的基础,并且支持链式调用。
  2. ​handle​​​方法是​​promise​​​内部的方法。​​then​​​方法传入的形参​​onFulfilled​​​以及创建新​​Promise​​​实例时传入的​​resolve​​​均被​​push​​​到当前​​promise​​​的​​callbacks​​​队列中,这是衔接当前​​promise​​​和后邻​​promise​​的关键所在(这里一定要好好的分析下handle的作用)。
  3. ​getUserId​​​生成的​​promise​​​(简称​​getUserId promise​​​)异步操作成功,执行其内部方法​​resolve​​​,传入的参数正是异步操作的结果​​id​
  4. 调用​​handle​​​方法处理​​callbacks​​​队列中的回调:​​getUserJobById​​​方法,生成新的​​promise​​​(​​getUserJobById promise​​)
  5. 执行之前由​​getUserId promise​​​的​​then​​​方法生成的新​​promise​​​(称为​​bridge promise​​​)的​​resolve​​​方法,传入参数为​​getUserJobById promise​​​。这种情况下,会将该​​resolve​​​方法传入​​getUserJobById promise​​​的​​then​​方法中,并直接返回。
  6. 在​​getUserJobById promise​​​异步操作成功时,执行其​​callbacks​​​中的回调:​​getUserId bridge promise​​​中的​​resolve​​方法
  7. 最后执行​​getUserId bridge promise​​​的后邻​​promise​​​的​​callbacks​​中的回调。

//例5
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(error, results) {
if (error) {
reject(error);
}
resolve(results.id)
})
})
}

getUserId().then(function(id) {
//一些处理
}, function(error) {
console.log(error)
})
有了之前处理fulfilled状态的经验,支持错误处理变得很容易,只需要在注册回调、处理状态变更上都要加入新的逻辑:
function Promise(fn) {
var state = 'pending',
value = null,
callbacks = [];

this.then = function (onFulfilled, onRejected) {
return new Promise(function (resolve, reject) {
handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
};

function handle(callback) {
if (state === 'pending') {
callbacks.push(callback);
return;
}

var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
ret;
if (cb === null) {
cb = state === 'fulfilled' ? callback.resolve : callback.reject;
cb(value);
return;
}
ret = cb(value);
callback.resolve(ret);
}

function resolve(newValue) {
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve, reject);
return;
}
}
state = 'fulfilled';
value = newValue;
execute();
}

function reject(reason) {
state = 'rejected';
value = reason;
execute();
}

function execute() {
setTimeout(function () {
callbacks.forEach(function (callback) {
handle(callback);
});
}, 0);
}

fn(resolve, reject);}

上述代码增加了新的​​reject​​​方法,供异步操作失败时调用,同时抽出了​​resolve​​​和​​reject​​​共用的部分,形成​​execute​​方法。

错误冒泡是上述代码已经支持,且非常实用的一个特性。在​​handle​​​中发现没有指定异步操作失败的回调时,会直接将​​bridge promise​​​(​​then​​​函数返回的​​promise​​​,后同)设为​​rejected​​​状态,如此达成执行后续失败回调的效果。这有利于简化串行​​Promise​​的失败处理成本,因为一组异步操作往往会对应一个实际功能,失败处理方法通常是一致的:

 

//例6
getUserId()
.then(getUserJobById)
.then(function (job) {
// 处理job
}, function (error) {
// getUserId或者getUerJobById时出现的错误
console.log(error);
});
function handle(callback) {
if (state === 'pending') {
callbacks.push(callback);
return;
}

var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
ret;
if (cb === null) {
cb = state === 'fulfilled' ? callback.resolve : callback.reject;
cb(value);
return;
}
try {
ret = cb(value);
callback.resolve(ret);
} catch (e) {
callback.reject(e);
}
}

如果在异步操作中,多次执行​​resolve​​​或者​​reject​​会重复处理后续回调,可以通过内置一个标志位解决。

总结

  1. 通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。
  2. 被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者