深入了解Promise

||
字数总计:4.5k|阅读时长:21分钟|阅读量:…
文章最后更新于 1053 天前,其中的信息可能已经过时。

本文主要讲解 Promise 的常用方法以及使用技巧,并实现如何手写一个 Promise。

Promise 的出现

回调地狱问题

在传统的 JS 编程中始终存在一个问题,即当当前回调函数的执行依赖于上一个回调函数的执行结果的时候,会形成回调函数层层嵌套的问题,严重影响代码的可读性与可维护性,这种现象一般称之为回调地狱。

下面为示例代码,回调地狱的一个比较常见的情景为ajax请求,即下一个请求的是否发起依赖于上一个请求的结果。

JS
let xhr = new XMLHttpRequest();
xhr.open(
"get",
"https://v0.yiketianqi.com/api?unescape=1&version=v61&appid=82294778&appsecret=4PKVFula&city=%E5%8C%97%E4%BA%AC"
);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.responseText);
//伪代码....
let xhr = new XMLHttpRequest();
xhr.open("get", "http://www.xx.com?a" + xhr.responseText);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.responseText);
}
}
};
}
}
};

Promise 的出现

  1. Promise是什么?其解决了什么问题? Promise 是异步编程的一种解决方案,比传统的解决方案回调函数更合理、更强大。ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。指定回调函数的方式也变得更加灵活易懂,也解决了异步回调地狱的问题旧方案是单纯使用回调函数,常见的异步操作有:定时器、fs 模块、ajax、数据库操作。从语法上说,Promise 是一个构造函数;从功能上说,Promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值。
  2. Promise设计的核心理念是什么?
    • 从Promise的角度来说,是将状态改变与回调函数彻底区分开。
    • 从应用Promise的角度来说,以ajax请求数据为例,则是将数据请求与数据处理区分开。

Promise 的实际应用

案例 1:利用 promise 来进行读取文件操作

JS
//1.普通文件读取方式
const fs = require("fs");
//2.直接利用readfile来进行读取
/* fs.readFile(__dirname + '/data.txt',(err,data)=>{
if(err) throw err;
console.log(data.toString());
}) */
//3.利用promise来实现文件的读取
const p = new Promise((resolve, reject) => {
fs.readFile(__dirname + "/data.txt", (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
p.then(
(value) => {
console.log(value.toString());
},
(reason) => {
console.log(reason);
}
);

案例 2:利用 promise 进行 ajax 请求

HTML
<body>
<button>发送ajax请求</button>
<script>
//1.获取DOM元素对象
let btn = document.querySelector("button");
//2.绑定事件
btn.onclick = function () {
//3.创建promise实例对象
const p = new Promise((resolve, reject) => {
//4.创建ajax实例对象
const xhr = new XMLHttpRequest();
//5.打开请求
xhr.open(
"get",
"https://www.yiketianqi.com/free/day?appid=82294778&appsecret=4PKVFula&unescape=1"
);
//6.发送请求
xhr.send();
//7.利用onreadystatechange事件
xhr.onreadystatechange = function () {
//8.判断
if (xhr.readyState == 4) {
if (xhr.status == 200) {
resolve(xhr.responseText);
} else {
reject(xhr.response);
}
}
};
});
p.then(
(value) => {
console.log(JSON.parse(value));
},
(reason) => {
console.log("获取信息失败");
}
);
};
</script>
</body>

案例 3:利用 promise 进行数据库操作

JS
const mongoose = require("mongoose");
new Promise((resolve, reject) => {
mongoose.connect("mongodb://127.0.0.1/project");
mongoose.connection.on("open", () => {
//连接成功的情况
resolve();
});
mongoose.connection.on("error", () => {
//连接失败的情况
reject();
});
}).then(
(value) => {
//创建结构
const NoteSchema = new mongoose.Schema({
title: String,
content: String,
});
//创建模型
const NoteModel = mongoose.model("notes", NoteSchema);
//读取操作
NoteModel.find().then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
},
(reason) => {
console.log("连接失败");
}
);

案例 4:封装一个函数,作用是读取文件

JS
const fs = require("fs");
function ReadFileFun(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
//判断
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
ReadFileFun("./data.txt").then(
(value) => {
console.log(value.toString());
},
(reason) => {
console.log(reason);
}
);

node 中的 promisify

  • promisify (只能在 NodeJS 环境中使用)
  • promisify 是 util 模块中的一个方法 util 是 nodeJS 的内置模块
  • 作用: 返回一个新的函数, 函数是 promise 风格的.
JS
const util = require("util");
const fs = require("fs");
//通过 fs.readFile 创建一个新的函数
const mineReadFile = util.promisify(fs.readFile);
mineReadFile("./resource/2.html").then(
(value) => {
console.log(value.toString());
},
(reason) => {
console.log(reason);
}
);
查看 promisify 的手写实现

看到 promisify 这个函数对其内部实现机制比较感兴趣,那我们就来手写一下。

  1. 首先promisify函数得返回一个函数,同时返回的这个函数应该返回一个Promise对象,根据这两点我们把函数的基本结构搭建起来。

    JS
    const promisify = () => {
    return function () {
    return new Promise((resolve, reject) => {});
    };
    };
  2. 利用返回的这个Promise对象,可以使用then方法进行回调的处理,即原来函数的回调决定这Promise状态的改变以及执行,所以需要将改变Promise对象的状态的回调函数传入参数中。

    JS
    /**
    * @param {(...args) => void} func
    * @returns {(...args) => Promise<any}
    */
    const promisify = (func) => {
    return function (...params) {
    return new Promise((resolve, reject) => {
    func.call(this, ...params, (err, data) => {
    if (err) reject(err);
    else resolve(data);
    });
    });
    };
    };

Promise 的实例方法

then

注意点:通过then返回的promise的对象的状态由谁决定。

  • 若返回非promise对象或者什么都不返回则,状态为fullfilled。
  • 若函数执行过程中抛出错误,则状态为rejected。
  • 若返回一个promise对象,则状态与返回的promise对象保持一致。
JS
const p = new Promise((resolve, reject) => {
resolve("ok");
});
let result = p.then(
(value) => {
throw "错误";
},
(reason) => {
console.log(reason);
}
);
console.log(result);

catch

能够穿透捕获Promise中的错误,其实相当于Promise.prototype.then(undefined, onRejected)。

JS
let p = new Promise((resolve, reject) => {
//resolve('success');
reject("error");
});
p.catch((reason) => {
console.log(reason);
});
//then方法中不是必须传入两个参数,可以只传递成功时的回调函数
//也可以单独使用catch来指定失败的回调函数
//异常(错误)穿透
//当如果有多个需要执行的成功时的回调函数,可以不需要每一次都写失败回调,可以统一最后利用catch
//当如果promise对象的状态为reject的话,会一直向下穿透直到catch方法
p.then((value) => {
console.log(value);
})
.then((value) => {
console.log(value);
})
.catch((reason) => {
console.log(reason);
});

finally

finally 是在 ES9(ES2018)中新增的一个特性:表示无论 Promise 对象变成 fufilled 还是 rejected 状态,最终都会被执行。 finally 方法中的回调函数是不接受参数的,因为无论前面是 fulfilled 状态还是 rejected 状态, 它都是执行。

JS
const p = new Promise((resolve, reject) => {
// resolve('ok');
reject("error");
});
p.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
})
.finally(() => {
console.log("finally");
});

Promise 类静态方法

resolve

  1. 当参数不是Promise对象的时候,返回promise状态为fullfilled。
  2. 当参数是Promise对象的时候,返回的Promise实例的属性由该Promise实例决定。
JS
let p3 = Promise.resolve(
new Promise((resolve, reject) => {
resolve("success");
})
);
console.log(p3);
let p4 = Promise.resolve(Promise.resolve(Promise.resolve("OK")));
console.log(p4);

reject

始终返回状态为rejected的Promise对象

JS
console.log(Promise.reject(123));
console.log(Promise.reject(Promise.resolve("ok")));

all

接受一个Promise对象的数组,如果数组中的Promise对象的状态都是fullfilled,则返回的也是一个状态为fullfilled对象,PromiseResult的值为每个成功值的数组。若存在状态为rejected的对象则返回一个状态为rejected的Promise对象,PromiseResult为第一个状态为rejected的Promise对象。

JS
let p1 = new Promise((resolve, reject) => {
resolve("ok");
});
let p2 = Promise.resolve("hello");
let p3 = Promise.resolve("oh yeah");
let result = Promise.all([p1, p2, p3]);
console.log(result);

race

和all方法类似都是接受一个Promise对象的数组,但其状态与结果由数组中最先改变的Promise对象决定。

JS
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 2000);
});
let p2 = Promise.resolve("success");
let p3 = Promise.resolve("oh hou");
let result = Promise.race([p1, p2, p3]);
console.log(result);

allSettled

与all方法类似,当数组内所有promise的状态都确定后执行成功的回调。基本上只有成功的回调。

JS
function ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
});
}

any

与all方法正好相反,只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfiilled 状态; 如果所有参数实例都变成 rejected,包装实例就会变成 rejected 状态,结果为一个AggregateError对象。

JS
let p1 = new Promise((resolve, reject) =>{" "}
{setTimeout(() => {
resolve("ok");
}, 1000)}
) let p2 = new Promise((resolve, reject) => {setTimeout(() => {
resolve("okk");
}, 2000)}) let p3 = new Promise((resolve, reject) => {setTimeout(() => {
reject("error");
}, 3000)}) Promise.any([p1, p2, p3]).then(res => {console.log(res)}).catch(err
=> {console.log("error")})

关键问题

中断 Promise 链

终止Promise链条,有且仅有一种方式,即返回一个状态为pending的promise对象。

JS
new Promise((resolve, reject) => {
resolve(111);
})
.then((value) => {
console.log(value);
console.log(222);
//
// return false;
// throw '出错啦';
//有且只有一种方式 返回一个pending状态的promise对象
return new Promise((resolve, reject) => {});
})
.then((value) => {
console.log(333);
})
.then((value) => {
console.log(444);
})
.catch((reason) => {
console.log(reason);
});

修改 Promise 的状态

JS
//如何修改 promise 对象状态
let p = new Promise((resolve, reject) => {
//1. resolve
// resolve('success');
//2. reject
// reject('error');
//3. 抛出错误 异常
// throw '出问题啦! 你说出这样的话 你没有良心!!';
// 状态的改变只有一次
resolve("ok");
reject("error");
});
console.log(p);

Promise 串联多个任务

JS
new Promise((resolve, reject) => {
console.log(111);
reject();
})
.then((value) => {
console.log(222);
})
.then((value) => {
console.log(value);
})
.then(
(value) => {
console.log(value);
},
(reason) => {
console.error(reason);
}
);

执行顺序分析

JS
setTimeout(() => {
console.log("0");
}, 0);
new Promise((resolve, reject) => {
console.log("1");
resolve();
})
.then(() => {
console.log("2");
new Promise((resolve, reject) => {
console.log("3");
resolve();
})
.then(() => {
console.log("4");
})
.then(() => {
console.log("5");
});
})
.then(() => {
console.log("6");
});
new Promise((resolve, reject) => {
console.log("7");
resolve();
}).then(() => {
console.log("8");
});
// 我们来分析一下这段代码执行结果的打印顺序,注意宏队列只会在微队列为空后才会执行
// 1. 同步执行:1 7
// 2. 异步队列中的宏队列与微队列出队列,进入执行栈执行:2 3 8
// 3. 4 比 6 先进微队列,:4 6
// 4. 5 出微队列后为空,宏队列开始执行:5 0

使用 JS 手写一个 promise

由于注释写的比较清除就不分步写了,注意Promise类的静态方法race等,接受的是一个可迭代对象,同时注意在写的时候所用到的技巧,foreach以及数组的原生方法。 值得注意的地方有:

  • _resolve的单独处理,处理嵌套的Promise对象。
  • 静态方法接受可迭代对象的方法大致相同,但注意空数组的时候返回什么。
  • 注意原生实现的时候then方法不能使用箭头函数定义,会出现this指向无法指向实例对象的问题。

原生实现

JS
// 首先使用一个自执行函数,避免全局污染
((window) => {
// 注意这里不能使用箭头函数,箭头函数不能用作构造函数
function Promise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
// 定义储存回调函数的数组
this.callbacks = [];
const _resolve = (value) => {
// 注意 Promise 的状态只能更改一次
if (this.PromiseState !== "pending") return;
// 这里需要对值是 promise 对象进行格外处理
if (value instanceof Promise) {
value.then(_resolve, _reject);
} else {
this.PromiseState = "fullfilled";
this.PromiseResult = value;
this.callbacks.forEach((callback) => {
callback.onResolved();
});
}
};
const _reject = (reason) => {
// 注意 Promise 的状态只能更改一次
if (this.PromiseState !== "pending") return;
this.PromiseState = "rejected";
this.PromiseResult = reason;
this.callbacks.forEach((callback) => {
callback.onRejected();
});
};
try {
executor(_resolve, _reject);
} catch (error) {
_reject(error);
}
}
Object.assign(Promise.prototype, {
// 在这里我们需要明确,then 方法需要返回一个 Promise 对象,并且需要对 onResolved 与 onRejected 是否是一个函数
then(onResolved, onRejected) {
if (!(onResolved instanceof Function)) onResolved = (value) => value;
if (!(onRejected instanceof Function))
onRejected = (reason) => {
throw reason;
};
return new Promise((resolve, reject) => {
// 抽离统一处理的方法
const __common = (callback) => {
// 通过 setTimeout 模拟异步回调的方法
setTimeout(() => {
// 还需要考虑到出错的情况
try {
// 注意我们现在需要通过这个的返回结果获取 then 方法返回的 Promise 的状态
const result = callback(this.PromiseResult);
if (result instanceof Promise) {
// 如果返回的是 Promise 对象,则状态由 Promise 对象决定,则将状态的改变手段,交予该对象的 then 的回调
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
});
};
// 这里 then 方法返回了一个 Promise 对象,其状态应该是由 onResolved 或 onRejected 的执行结果决定的,这里我们首先先判断执行哪一个
if (this.PromiseState === "fullfilled") {
__common(onResolved);
} else if (this.PromiseState === "rejected") {
__common(onRejected);
}
// 同时,这里只考虑到,状态改变后执行回调的情况,没有考虑回调定义在状态改变之前的情况,这里我们来考虑 pending 的情况
else if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: () => __common(onResolved),
onRejected: () => __common(onRejected),
});
}
});
},
catch(onRejected) {
return this.then(undefined, onRejected);
},
finally(callback) {
// finally 方法是无论如何都会执行并返回一个等效的 Promise 对象
return this.then(
(value) => {
return Promise.resolve(callback()).then(() => value);
},
(reason) => {
return Promise.resolve(callback()).then(() => {
throw reason;
});
}
);
},
});
// 定义类身上的静态方法
// 定义 resolve 方法
Promise.resolve = (value) => {
return new Promise((resolve, reject) => {
if (value instanceof Promise) value.then(resolve, reject);
else resolve(value);
});
};
// 定义 reject 方法
Promise.reject = (reason) => {
return new Promise((resolve, reject) => {
reject(reason);
});
};
// 定义 all 方法
Promise.all = (iterable) => {
if (!iterable[Symbol.iterator]) {
throw new TypeError("Argument must be iterable");
}
return new Promise((resolve, reject) => {
// all 方法则为,当所有状态为 fullfilled 的才会返回成功,有 rejected 就会返回失败
// 将可迭代对象转换为数组
const promises = Array.from(iterable);
// 如果数组为空,直接返回一个已成功的Promise对象,值为一个空数组
if (promises.length === 0) {
return resolve([]);
}
const length = promises.length;
let arr = new Array(length);
let count = 0;
for (let i = 0; i < length; i++) {
if (promises[i] instanceof Promise) {
promises[i].then((value) => {
arr[i] = value;
count++;
if (count === length) resolve(arr);
}, reject);
} else {
arr[i] = promises[i];
count++;
if (count === length) resolve(arr);
}
}
});
};
Promise.race = (iterable) => {
if (!iterable[Symbol.iterator]) {
throw new TypeError("Argument must be iterable");
}
return new Promise((resolve, reject) => {
const promises = Array.from(iterable);
if (promises.length === 0) {
return;
}
for (let i = 0; i < promises.length; i++) {
if (promises[i] instanceof Promise) {
promises[i].then(resolve, reject);
} else {
resolve(promises[i]);
}
}
});
};
// promise.allSettled方法
Promise.allSettled = function (iterable) {
// 参数必须是一个可迭代对象
if (!iterable[Symbol.iterator]) {
throw new TypeError("Argument must be iterable");
}
// 返回一个新的promise对象
return new Promise((resolve, reject) => {
// 将可迭代对象转换为数组
const promises = Array.from(iterable);
// 如果数组为空,直接返回一个已完成的promise,值为一个空数组
if (promises.length === 0) {
resolve([]);
return;
}
// 定义一个结果数组,用来存放每个promise的状态和值或原因
const results = new Array(promises.length);
// 定义一个计数器,用来记录已完成的promise的数量
let count = 0;
// 遍历每个promise
promises.forEach((promise, index) => {
// 将非promise对象转换为promise对象
Promise.resolve(promise)
// 如果成功,将结果对象存入数组,并增加计数器
.then((value) => {
results[index] = { status: "fulfilled", value };
count++;
// 如果所有的promise都完成了,就返回一个已完成的promise,值为结果数组
if (count === promises.length) {
resolve(results);
}
})
// 如果失败,同样将结果对象存入数组,并增加计数器
.catch((reason) => {
results[index] = { status: "rejected", reason };
count++;
// 如果所有的promise都完成了,就返回一个已完成的promise,值为结果数组
if (count === promises.length) {
resolve(results);
}
});
});
});
};
// promise.any方法
Promise.any = function (iterable) {
// 参数必须是一个可迭代对象
if (!iterable[Symbol.iterator]) {
throw new TypeError("Argument must be iterable");
}
// 返回一个新的promise对象
return new Promise((resolve, reject) => {
// 将可迭代对象转换为数组
const promises = Array.from(iterable);
// 如果数组为空,直接返回一个已拒绝的promise,原因为一个空的AggregateError
if (promises.length === 0) {
reject(new AggregateError([]));
return;
}
// 定义一个错误数组,用来存放每个promise的失败原因
const errors = new Array(promises.length);
// 定义一个计数器,用来记录已拒绝的promise的数量
let count = 0;
// 遍历每个promise
promises.forEach((promise, index) => {
// 将非promise对象转换为promise对象
Promise.resolve(promise)
// 如果成功,直接返回一个已完成的promise,值为该promise的值
.then((value) => {
resolve(value);
})
// 如果失败,将失败原因存入错误数组,并增加计数器
.catch((reason) => {
errors[index] = reason;
count++;
// 如果所有的promise都拒绝了,就返回一个已拒绝的promise,原因为一个包含错误数组的AggregateError
if (count === promises.length) {
reject(new AggregateError(errors));
}
});
});
});
};
// 使用自定义的 Promise 替换掉默认的 Promise
window.Promise = Promise;
})(window);

class 实现

原生的都写出来了只能说class版本有手就行,注意class field写法的方法仍然属于实例属性,class field的主要设计目的就是为了解决在类里面使用this的种种不便,精简了部分代码。

JS
class Promise {
constructor(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
this.callbacks = [];
const _resolve = (value) => {
if (this.PromiseState !== "pending") return;
if (value instanceof Promise) value.then(_resolve, _reject);
else {
this.PromiseState = "fullfilled";
this.PromiseResult = value;
this.callbacks.forEach((callback) => callback.onResolved());
}
};
const _reject = (reason) => {
if (this.PromiseState !== "pending") return;
this.PromiseState = "rejected";
this.PromiseResult = reason;
this.callbacks.forEach((callback) => callback.onRejected());
};
try {
executor(_resolve, _reject);
} catch (error) {
_reject(error);
}
}
then(onResolved, onRejected) {
if (!(onResolved instanceof Function)) onResolved = (value) => value;
if (!(onRejected instanceof Function))
onRejected = (reason) => {
throw reason;
};
return new Promise((resolve, reject) => {
const _common = (callback) => {
setTimeout(() => {
try {
const result = callback(this.PromiseResult);
if (result instanceof Promise) result.then(resolve, reject);
else resolve(result);
} catch (error) {
reject(error);
}
});
};
if (this.PromiseState === "fullfilled") {
_common(onResolved);
} else if (this.PromiseState === "rejected") {
_common(onRejected);
} else if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: () => _common(onResolved),
onRejected: () => _common(onRejected),
});
}
});
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
finally(callback) {
return this.then(
(value) => Promise.resolve(callback()).then(() => value),
(reason) =>
Promise.resolve(callback()).then(() => {
throw reason;
})
);
}
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) value.then(resolve, reject);
else {
resolve(value);
}
});
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
static all(iterables) {
if (!iterables[Symbol.iterator]) {
throw new TypeError(
`TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
);
}
return new Promise((resolve, reject) => {
const promises = Array.from(iterables);
if (promises.length === 0) return resolve([]);
const results = new Array(promises.length);
let count = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise).then((value) => {
results[index] = value;
count++;
if (count === promises.length) resolve(results);
}, reject);
});
});
}
static race(iterables) {
if (!iterables[Symbol.iterator]) {
throw new TypeError(
`TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
);
}
return new Promise((resolve, reject) => {
const promises = Array.from(iterables);
promises.forEach((promise) => {
Promise.resolve(promise).then(resolve, reject);
});
});
}
static allSettled(iterables) {
if (!iterables[Symbol.iterator]) {
throw new TypeError(
`TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
);
}
return new Promise((resolve, reject) => {
const promises = Array.from(iterables);
if (promises.length === 0) return resolve([]);
const results = new Array(promises.length);
let count = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise).then(
(value) => {
results[index] = { status: "fullfilled", value };
count++;
if (count === promises.length) resolve(results);
},
(reason) => {
results[index] = { status: "rejected", reason };
count++;
if (count === promises.length) resolve(results);
}
);
});
});
}
static any(iterables) {
if (!iterables[Symbol.iterator]) {
throw new TypeError(
`TypeError: ${typeof iterables} ${iterables} is not iterable (cannot read property Symbol(Symbol.iterator))`
);
}
return new Promise((resolve, reject) => {
const promises = Array.from(iterables);
if (promises.length === 0) return reject(new AggregateError([]));
const results = new Array(promises.length);
let count = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise).then(resolve, (reason) => {
results[index] = reason;
count++;
if (count === promises.length) reject(new AggregateError(results));
});
});
});
}
}

评论

评论加载中…

站内搜索