executor
Promise对象的构造器(constructor)语法如下:
let promise = new Promise(function(resolve, reject) {
// executor(生产者代码,“歌手”)
});
传递给 new Promise 的函数被称为executor(执行者)。当 new Promise 被创建,executor 会自动运行并尝试执行一项工作。尝试结束后,如果成功则调用 resolve,如果出现 error 则调用 reject。
由 new Promise 构造器返回的 promise 对象具有以下内部属性:
- state — 最初是 "pending",然后在 resolve 被调用时变为 "fulfilled",或者在 reject 被调用时变为 "rejected"。
- result — 最初是 undefined,然后在 resolve(value) 被调用时变为 value,或者在 reject(error) 被调用时变为 error。
所以,executor 最终将 promise 移至以下状态之一:
let promise = new Promise(function(resolve, reject) {
// 当 promise 被构造完成时,自动执行此函数
// 1 秒后发出工作已经被完成的信号,并带有结果 "done"
setTimeout(() => resolve("done"), 1000);
});
通过运行上面的代码,我们可以看到两件事儿:
- executor 被自动且立即调用(通过 new Promise)。
- executor 接受两个参数:resolve 和 reject。这些函数由 JavaScipt 引擎预先定义,因此我们不需要创建它们。我们只需要在 executor 准备好时调用其中之一即可。
then,catch,finally
- then 语法如下:
promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);
.then的第一个参数是一个函数,该函数将在promise resolved后运行并接收结果。
.then的第二个参数也是一个函数,该函数将在promise rejected后运行并接收 error。
如果我们只对成功完成的情况感兴趣,那么我们可以只为 .then 提供一个函数参数:
let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(alert); // 1 秒后显示 "done!"
- catch 错误处理下面两种方式是一样的:
- 可以使用 null 作为第一个参数:
.then(null, errorHandlingFunction) - 使用
.catch(errorHandlingFunction)
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// .catch(f) 与 promise.then(null, f) 一样
promise.catch(alert); // 1 秒后显示 "Error: Whoops!"
.catch(f)调用是.then(null, f)的完全的模拟,它只是一个简写形式。
- finally
- finally 处理程序(handler)没有参数。在 finally 中,我们不知道 promise 是否成功。没关系,因为我们的任务通常是执行“常规”的定稿程序(finalizing procedures)。
- 例如无论如何,都停止使用不再需要的loading或者indicator。
- finally 是执行清理(cleanup)的很好的处理程序。
- finally 处理程序将结果和 error 传递给下一个处理程序。
new Promise((resolve, reject) => {
/* 做一些需要时间的事儿,然后调用 resolve/reject */
})
// 在 promise 为 settled 时运行,无论成功与否
.finally(() => stop loading indicator)
// 所以,加载指示器(loading indicator)始终会在我们处理结果/错误之前停止
.then(result => show result, err => show error)
以上:
记住以下细节:
.then接收两个参数,可以只传一个参数;.catch(f)调用是.then(null, f)的完全的模拟,它只是一个简写形式;.finally是无论成功与否都会执行,并且不影响结果和error往下一个处理程序传递。
Promise类的5种静态方法
- Promise.all(promises) —— 等待所有 promise 都 resolve 时,返回存放它们结果的数组。如果给定的任意一个 promise 为 reject,那么它就会变成 Promise.all 的 error,所有其他 promise 的结果都会被忽略。
- 如果出现 error,其他 promise 将被忽略
- Promise.all(...) 接受可迭代对象(iterable)的 promise(大多数情况下是数组)。但是,如果这些对象中的任意一个都不是 promise,那么它将被“按原样”传递给结果数组。
- Promise.allSettled(promises)(ES2020 新增方法)—— 等待所有 promise 都 settle 时,并以包含以下内容的对象数组的形式返回它们的结果:
- status: "fulfilled" 或 "rejected"
- value(如果 fulfilled)或 reason(如果 rejected)。
- Promise.race(promises) —— 等待第一个 settle 的 promise,并将其 result/error 作为结果。
- Promise.resolve(value) —— 使用给定 value 创建一个 resolved 的 promise。
- 当一个函数被期望返回一个 promise 时,这个方法用于兼容性。(白话:可以用这个方法
Promise.resolve(value)将value “封装”进 promise,并返回一个promise)
- 当一个函数被期望返回一个 promise 时,这个方法用于兼容性。(白话:可以用这个方法
- Promise.reject(error) —— 使用给定 error 创建一个 rejected 的 promise。
微任务
Promise 的处理程序(handlers).then、.catch 和 .finally 都是异步的。
自己实现Promise.all
function PromiseAll(promiseArray) {
// 1. promise.all:返回的是 Promise
return new Promise((resolve, reject) => {
// 2. 严谨判断:是否传入的参数是数组
if(!Array.isArray(promiseArray)) {
return reject(new Error('传入的参数必须是数组'));
}
const res = [];
let counter = 0;//记录执行完了几个promise
const promiseNums = promiseArray.length;
for(let i = 0; i < promiseNums; i++) {
// 3. 注意:传入的参数不一定是Promise,需要做判断
// const isPromise = Object.prototype.toString.call(promiseArray[i] === '[object Promise]');
// if(isPromise){
// promiseArray[i].then(result => {
// res.push(result)
// })
// }else{
// res.push(promiseArray[i])
// }
// 上面这种做法的缺点:对于结果的处理需要写两遍
// 下面的做法:Promise.resolve(params)这个参数params会被自动转换成promise
Promise.resolve(promiseArray[i]).then(value => {
// 4. 错误做法:忽略Promise.all的顺序问题,输入的是什么顺序,返回就是什么顺序。
// push方法取决于执行快慢,快的就立即push,顺序可能混乱
// res.push(value)
// if(res.length === promiseNums){
// resolve(res)
// }
counter ++; // 每一个promise执行完后counter+1
res[i] = value; // 给res通过索引进行赋值
if(counter === promiseNums){
resolve(res)
}
// 5. 不能使用res.length === promiseNums来判断
// 因为数组的特性
// const array = [];
// array[6] = 'xxxx'; 给最后一个索引赋值,会按照最后一个索引这么长的空间
// console.log(array.length); //7
}).catch(e => reject(e));
}
})
}
const pro1 = new Promise((res,rej)=>{
setTimeout(() => {
res('1')
}, 1000);
})
const pro2 = new Promise((res,rej)=>{
setTimeout(() => {
res('2')
}, 1000);
})
const pro3 = new Promise((res,rej)=>{
setTimeout(() => {
res('3')
}, 1000);
})
const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => {
console.log(res) // 3秒之后打印['1','2','3']
}).catch((e)=>{
console.log(e)
})