导读:Promise 是异步编程的一种解决方案,比传统的回调函数或事件更合理和更灵活。本文主要展示 Promise 提供的方法列表,以及基本的实现原理。通过本文,我们能加深 Promise 方法的理解和场景使用,对 Promise.all、Promise.race、Promise.allSettled 和 Promise.any 四个方法的异同之处也有更深层次的领悟。
Promise 的实例方法有 then/catch/finally 三种,静态方法有 all/race/allSettled/any/resolve/reject 六种。其中 then 实现起来因为涉及 Promise 裁决过程(The Promise Resolution Procedure),会比较复杂,其余的都是基于已有功能的拓展。下面都给大伙列出所有的方法。
then 方法是整个 Promise 解决的核心内容,同时因为回调函数和返回一个新的 Promise 实例,因此决议过程比较复杂。
// class MyPromise {
// static PENDING = 'pending'; // 进行中
// static FULFILLED = 'fulfilled'; // 已成功
// static REJECTED = 'rejected'; // 已失败
// state = MyPromise.PENDING;
// value = null;
// reason = null;
// onFulfilledCallbacks = [];
// onRejectedCallbacks = [];
// ...
// }
MyPromise.prototype.then = (onFulfilled, onRejected) => {
if (typeof onFulfilled != 'function') {
onFulfilled = (value) => value;
}
if (typeof onRejected != 'function') {
onRejected = (reason) => {
throw reason;
};
}
// Promise 核心解决过程 见规范2.3
const _resolvePromise = (promise, x, resolve, reject) => {
// 2.3.1 如果 promise 和 x 指向同一对象,抛出 TypeError 错误
if (promise === x) {
const errMsg = 'The promise and the return value are the same';
return reject(new TypeError(errMsg));
}
// 2.3.3 如果 x 为对象(不是null)或者函数
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let then = null;
try {
// 2.3.3.1. 检索属性 x.then
then = x.then;
} catch (error) {
// 2.3.3.2 如果 x.then 导致抛出异常 e,则以 e 为拒绝原因拒绝 promis
return reject(error);
}
// 2.3.3.3 如果 then 是一个函数,x 作为 then 的 this 调用该方法,第一个参数是成功的回调函数,第二个参数是失败的回调函数
if (typeof then === 'function') {
let called = false;
try {
then.call(
x,
(y) => {
// 2.3.3.3.4 如果成功回调与失败回调都被调用或多次调用同一个参数,则第一个调用优先,其他调用都将被忽略。
if (called) return;
called = true;
// 2.3.3.3.1 如果成功回调以值 y 调用,运行 [[Resolve]](promise,y)
_resolvePromise(promise, y, resolve, reject);
},
(r) => {
// 2.3.3.3.4 如果成功回调与失败回调都被调用或多次调用同一个参数,则第一个调用优先,其他调用都将被忽略。
if (called) return;
called = true;
// 2.3.3.3.2 如果失败回调以原因 r 调用,用 r 拒绝 promise
reject(r);
}
);
} catch (error) {
// 2.3.3.4 如果调用 then 方法抛出异常 e:
// 2.3.3.4.1 若成功回调或失败回调都调用过,忽略
if (called) return;
// 2.3.3.4.2 未调用,用 e 作为原因拒绝 promise
reject(error);
}
} else {
// 2.3.3.4. 如果 then 不是函数,用 x 作为值完成 promise
return resolve(x);
}
} else {
// 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
return resolve(x);
}
};
// 链式返回的Promise
const newPromise = new MyPromise((resolve, reject) => {
switch (this.state) {
case MyPromise.FULFILLED:
setTimeout(() => {
try {
const x = onFulfilled(this.value);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
break;
case MyPromise.REJECTED:
setTimeout(() => {
try {
const x = onRejected(this.reason);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
break;
case MyPromise.PENDING:
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
_resolvePromise(newPromise, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
});
break;
}
});
return newPromise;
};
如果上面没有定义 reject 方法或者在抛出错误,那么所有的异常会走向 catch 方法,而 catch 可以复用 then 方法。
MyPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
不管是 resolve 还是 reject 都会调用 finally 。那么相当于 fianlly 方法替使用者分别调用了一次 then 的 resolved 和 rejected 状态回调。
MyPromise.prototype.finally = function (fn) {
return this.then(
(value) => {
fn();
return value;
},
(reason) => {
fn();
throw reason;
}
);
};
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。所有参数数组 Promise 实例执行 resolve 回调后,新实例执行 resolve 回调;如果中间有任何一个 Promise 实例执行 reject 回调,那么新实例就直接执行 reject 回调了。
打个比方:多名员工分别同时进行多个项目,你要求任何一个项目都必须是令你满意的,有一个不满意都算这件事(所有项目)是失败的。强调的是整体令你满意。
MyPromise.all = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve([]);
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
result[i] = data;
if (++index === promises.length) {
resolve(result);
}
},
(err) => {
reject(err);
return;
}
);
}
}
});
};
Promise.race() 顾名思义,就是竞赛,返回最快完成那一个 Promise 实例。只要参数数组中有一个 Promise 实例执行 resolve 回调或 reject 回调后,新实例就直接返回结果。
打个比方:多名员工分别同时进行多个项目,你只要最快完成的项目,无论项目是否令你满意。强调的是要快。
MyPromise.race = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve();
} else {
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(data) => {
resolve(data);
},
(err) => {
reject(err);
return;
}
);
}
}
});
};
Promise.allSettled() 方法只有等到参数数组的所有 Promise 实例都发生状态变更,返回的 Promise 实例才会发生状态变更,无论是执行 resolve 回调还是 reject 回调的状态。
打个比方:多名员工分别同时进行多个项目,你要求每个一个项目都必须完成,然后得到所有项目是令你满意还是令你不满意的。强调的是最终结果。
MyPromise.allSettled = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve([]);
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i]
.then(
(value) => {
result[i] = {
status: 'fulfilled',
value,
};
},
(reason) => {
result[i] = {
status: 'rejected',
reason,
};
}
)
.finally(() => {
if (++index === promises.length) {
return resolve(result);
}
});
}
}
});
};
同时因为 Promise.allSettled() 和 Promise.all() 都是对所有 Promise 实例的一种处理,下面就可以利用 Promise.all() 来实现 Promise.allSettled() 方法。
MyPromise.allSettled = function (promises) {
return Promise.all(
promises.map((item) =>
Promise.resolve(item).then(
(value) => ({ status: 'fulfilled', value }),
(reason) => ({ status: 'rejected', reason })
)
)
);
};
Promise.any() 方法是返回任意一个最快执行 resolve 回调的 Promise 实例。
打个比方:多名员工同时进行多个项目,你只要最快得到令你满意的那个项目,无论所有项目最终都没令你满意,这件事(所有项目)才算是失败了。强调的是又快又好。
MyPromise.any = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return resolve();
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(value) => {
return resolve(value);
},
(reason) => {
result[i] = reason;
if (++index === promises.length) {
return reject(new AggregateError(result));
}
}
);
}
}
});
};
Promise.resolve 方法返回一个以给定值解析后的 Promise 实例。相当于执行 then 方法里面的 _resolvePromise。
MyPromise.reject = function (value) {
return new MyPromise((resolve, reject) => {
resolve(value);
});
};
Promise.reject 方法返回一个带有拒绝原因的 Promise 实例。
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
作者:Hey_Major
链接:https://juejin.cn/post/7094273360041312264
我们知道可以用call(), apply() 和 bind()这三个函数都是用来完成函数调用,并且设置this指向。 call()和apply()是 ECMAScript3 标准引入,而bind()函数则是在 ECMAScript 5 引入。
可能这个疑惑只在像我这种从mongoose要切换到sequelize的人身上出现。最近有个项目要升级,并且数据库要从mongodb迁移到mysql,于是要学习使用sequelize,准备替换原来的mongose。
使用VUE做demo,需要用弹出框,在网上找了一下,找了一个能用的,把使用方法总结一下,备忘
我们都听过知其然知其所以然这句话:那么不知道大家是否思考过new Vue()这个过程中究竟做了些什么?过程中是如何完成数据的绑定,又是如何将数据渲染到视图的等等
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!