什么是Promise?
Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
Promise的实现
Promises/A+规范原文:https://promisesaplus.com/
- 构建Promise类
进行初始化,设置status为pending,data和err都为undefined,设置两个函数resolve和reject传递数据,executor默认先执行一次,具体流程看代码
constructor(executor) {
/* 初始化 */
this.status = 'pending';/* 当前状态,用于控制resolve和reject的执行 */
this.data = undefined;/* 记录成功数据 */
this.err = undefined;/* 记录失败数据 */
/* 成功 */
let resolve = data => {
if (this.status === 'pending') {
this.data = data;
/* 改变为成功状态后reject将不会执行 */
this.status = 'fulfilled';
}
}
/* 失败 */
let reject = err => {
if (this.status === 'pending') {
this.err = err;
this.status = 'rejected';
}
}
/* 默认执行一次 */
try {
/* 执行executor,通过resolve/reject把data/err传给当前Promise对象 */
executor(resolve, reject);
} catch (e) {
reject(e);/* 执行出错将把错误传给this.err */
}
}
- 通过获取data/err的方式传递数据给then的两个函数,实现同步情况下的then函数功能
then(onfulfilled, onrejected) {
/* fufilled状态说明this.data已经赋值,直接传参即可 */
if (this.status === 'fulfilled') {
onfulfilled(this.data);
}
/* rejected状态说明this.err已经赋值,直接传参即可 */
if (this.status === 'rejected') {
onrejected(this.err);
}
}
// 示例:
let p = new Promise((resolve, reject) => { reject('hello') })
p.then(
data => { console.log(`data->${data}`) },
err => { console.log(`err->${err}`) });
// 结果:err->hello
- 根据发布订阅的原理,数组存储回调函数,等待异步执行resolve/reject再触发回调函数,实现异步情况下的then函数功能
class Promise {
constructor(executor) {
//省略...
this.onfulfilled_cbs = [];/* 成功回调 */
this.onrejected_cbs = [];/* 失败回调 */
/* 成功 */
let resolve = data => {
if (this.status === 'pending') {
this.data = data;
/* 改变为成功状态后reject将不会执行 */
this.status = 'fulfilled';
/* 执行等待的回调 */
this.onfulfilled_cbs.forEach(cb => cb());
}
}
/* 失败 */
let reject = err => {
if (this.status === 'pending') {
this.err = err;
this.status = 'rejected';
this.onrejected_cbs.forEach(cb => cb());
}
}
//省略...
}
then(onfulfilled, onrejected) {
//省略代码...
/* 当为pending态时,将回调存入对应数组,等待异步执行完再依次触发 */
if (this.status === 'pending') {
this.onfulfilled_cbs.push(() => {
onfulfilled(this.data);
})
this.onrejected_cbs.push(() => {
onrejected(this.err);
})
}
}
}
// 示例:
let p = new Promise((resolve, reject) => {
setTimeout(() => { reject('hello') }, 3000)
})
p.then(
data => { console.log(`data->${data}`) },
err => { console.log(`err->${err}`) });
// 结果:err->hello
- 实现then函数的链式调用
(1)实现每个then函数执行后返回一个新的Promise对象nextPromise,只有这样才能不断产生和执行新的then函数
(2)记录当前then里面两个回调的返回值,返回值即可能是普通值也可能是Promise对象,需要将其处理成为普通值再传给nextPromise
a.当then两个回调为空或者不是函数时,设置默认函数
b.为了解决对象未创建完成前无法对其进行赋值的问题,我们需要将处理的过程放到一个异步api里面,采用异步方式对nextPromise赋值
then(onfulfilled, onrejected) {
/* 设置默认 */
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data;
onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err };
/* 创建新对象 */
let nextPromise = new Promise1((resolve, reject) => {
/* fufilled状态说明this.data已经赋值,直接传参即可 */
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
/* 记录返回值 */
let res = onfulfilled(this.data);
/* 对返回值处理后传入新的对象 */
resolvePromise(nextPromise, res, resolve, reject);
} catch (e) {
/* 如果出错直接捕获传给nextPromise */
reject(e);
}
})
}
/* rejected状态说明this.err已经赋值,直接传参即可 */
if (this.status === 'rejected') {
setTimeout(() => { /*省略...*/ })
}
/* 当为pending态时,将回调存入对应数组,等待异步执行完再依次触发 */
if (this.status === 'pending') {
this.onfulfilled_cbs.push(() => {
setTimeout(() => { /*省略...*/ })
})
this.onrejected_cbs.push(() => {
setTimeout(() => { /*省略...*/ })
})
}
})
return nextPromise;
}
(3)对res的类型进行检测并处理,对Promise向下递归直到res为普通类型后传递给nextPromise,设置called防止测试出现错误。
let resolvePromise = (nextPromise, res, resolve, reject) => {
/* 根据Promise/A+上的要求,如果返回值和新对象是同一个,直接报错 */
if (nextPromise === res) {
return reject(new TypeError(new TypeError('Chaining cycle detected for promise #<Promise>')))
}
/* 判断是对象或者函数 */
if (typeof res === 'object' && res !== null || typeof res === 'function') {
let called = false;//测试时 可能用两个版本的Promise 设置以防止防止失败后调用成功
/* 文档要求防止取then出错 */
try {
/* 获取res.then进一步判断 */
let then = res.then;
/* then是函数说明res是Promise对象 */
if (typeof then === 'function') {
/* 执行then */
then.call(res, data => {
if (called) return;
called = true;
/* 递归直到非Promise对象再传给resolve */
resolvePromise(nextPromise, data, resolve, reject);
}, err => {/* 继续传递错误 */
if (called) return;
called = true;
reject(err);
})
} else {/* 普通对象直接传值 */
if (called) return;
called = true;
resolve(res);
}
} catch (e) {/* 运行错误直接给reject */
if (called) return;
called = true;
reject(e);
}
} else { resolve(res); }/* 普通类型直接返回 */
}
// 示例:
let p = new Promise((resolve, reject) => {
resolve('hello')
})
let p2 = p.then(data => {
console.log(`data->${data}`);
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('999');
})
})
}, err => console.log(`err->${err}`))
p2.then(data => console.log(`data2->${data}`),
err => console.log(`err2->${err}`))
// 结果:data->hello err2->999
- 使用promises-aplus-tests库并进行测试
安装:npm install promises-aplus-tests -g
运行:promises-aplus-tests 当前js文件路径
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
module.exports = Promise;
- 实现Promise.all(),遍历传入的任务数组,若为Promise对象,则执行then函数存返回值,若为普通值,直接存入要传回的数组。
function isPromise(p) {
if (typeof p === 'object' && p !== null || typeof p === 'function') {
if (typeof p.then === 'function') {
return true;
}
}
return false;
}
Promise.all = (tasks) => {
return new Promise((resolve, reject) => {
let results = [], index = 0;
function processData(key, value) {
results[key] = value;
/* index计数,在异步操作时key小的会在后面执行 */
if (++index === tasks.length) {
resolve(results);
}
}
for (let i = 0; i < tasks.length; i++) {
let task = tasks[i];
if (isPromise(task)) {
task.then(data => processData(i, data));
} else {
processData(i, task);
}
}
})
}
//示例:
function say(str) {
let dfd = Promise.defer();
setTimeout(() => dfd.resolve(str));
return dfd.promise;
}
Promise.all([1, 2, 3, say('hello'), 5, 6, 7, say('bye')])
.then(data => console.log(data),
err => console.log(err))
// [ 1, 2, 3, 'hello', 5, 6, 7, 'bye' ]
- 其他功能有时间再写…