Promise 初体验
传统地使用回调进行异步操作,容易造成回调地狱的问题:
javascript
require("fs").readFile("foo.txt", (err, data) => {});
$.get("/server", (data) => {});
setTimeout(() => {}, 200);
从语法上来说:Promise 是一个构造函数 ,它接受一个函数 作为参数,而这个函数的2个参数 又必须是函数,分别是 resolve 和 reject
从功能上来说:Promise 对象用于封装一个异步操作并可以获取其成功/失败的结果值,它的设计思想是,所有异步任务都返回一个 Promise 实例
Promise 支持链式调用,可以解决回调地狱的问题
javascript
function f1(resolve, reject) { // resolve, reject 都是函数
// 异步代码...
}
var p1 = new Promise(f1);
Promise 实例有一个then方法,用来指定下一步的回调函数
javascript
var p1 = new Promise(f1);
p1.then(f2);
Promise 对象有 3 种状态:
- 异步操作未完成(pending)
- 异步操作成功(fulfilled)
- 异步操作失败(rejected)
then方法可以接受两个回调函数,第一个是异步操作成功时(变为fulfilled状态)的回调函数,第二个是异步操作失败(变为rejected)时的回调函数(该参数可以省略)。一旦 Promise 状态发生改变,就调用相应的回调函数
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>Promise 初体验</h2>
<button id="btn">点击抽奖</button>
<script>
function rand(m, n) {
return Math.ceil(Math.random() * (n - m + 1)) + m - 1;
}
const btn = document.querySelector("#btn");
btn.addEventListener("click", function() {
const p = new Promise((resolve, reject) => {
// 异步任务
setTimeout(() => {
let n = rand(1, 100);
if (n <= 30) {
resolve(n); // 将成功状态返回
} else {
reject(n); // 将失败状态返回
}
}, 1000);
});
p.then(
(value) => { // resolve
alert("恭喜中奖: n = " + value);
},
(reason) => { // reject
alert("再接再厉: n = " + reason);
}
);
});
</script>
</body>
</html>
Promise实践练习
fs 读取文件
原始版本:
javascript
const fs = require("fs");
let path = "foo.txt";
fs.readFile(path, (err, data) => {
if (err) throw err;
console.log(data.toString());
})
使用 Promise 封装异步任务:
javascript
const fs = require("fs");
let path = "foo.txt";
let p = new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
else resolve(data.toString());
});
});
p.then(value => console.log(value), reason => console.warn(reason));
其实异步任务这样写也可以:
javascript
let p = new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
resolve(data); // 没有 else
});
});
虽然看起来如果 err 了,resolve 也会"错误地"执行,但事实上,由于 Promise 的状态只会被改变一次,所以如果 err 了下面的 resolve 是不会执行的
AJAX 请求
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>Promise 封装 ajax 请求</h2>
<button class="btn">点击发送 ajax</button>
<script>
const btn = document.querySelector(".btn");
btn.addEventListener("click", function () {
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.mxnzp.com/api/jokes/list/random");
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
});
p.then(
value => console.log(value),
reason => console.warn(reason)
);
});
</script>
</body>
</html>
封装 readFile
手动封装
javascript
function myReadFile(path) {
return new Promise((resolve, reject) => {
require("fs").readFile(path, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
myReadFile("foo.txt").then(
value => console.log(value.toString()),
reason => console.log(reason)
);
使用 util.promisify 封装
采用遵循常见的错误优先的回调风格的函数(也就是将 (err, value) => ... 回调作为最后一个参数),并返回一个返回 Promise 的版本。
javascript
const util = require("util");
const fs = require("fs");
myReadFile = util.promisify(fs.readFile);
myReadFile("foo.txt").then(
value => console.log(value.toString()),
reason => console.log(reason)
);
封装 AJAX
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
});
}
sendAJAX("https://www.mxnzp.com/api/jokes/list/random").then(
value => console.log(value),
reason => console.warn(reason)
);
</script>
</body>
</html>
PromiseState
Promise 状态:实例对象的一个属性 :PromiseState
这个状态只能改变一次
PromisResult
异步任务成功或失败的结果,也就是 then() 的2个函数的参数:value & reason
Promise 流程
Promise API
Promise 构造函数
javascript
let executor = (resolve, reject) => {} // 执行器
Promise(executor) {}
resolve 函数:内部定义成功时我们调用的函数 value => {}
reject 函数:内部定义失败时我们调用的函数 reason => {}
executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
then
javascript
let onResolved = (value) => {}; // 成功的回调
let onRejected = (reason) => {}; // 失败的回调
Promise.prototype.then(onResolved, onRejected);
then 会返回一个新的 Promise 对象
catch
只在失败时执行
javascript
let onRejected = (reason) => {}
Promise.prototype.catch(onRejected)
html
<body>
<script>
let p = new Promise((resolve, reject) => {
reject("Error");
});
p.catch(
reason => console.warn(reason),
);
</script>
</body>
resolve
这个方法属于 Promise
函数对象,并不属于实例对象
它返回一个成功/失败的 Promise 对象(快速封装得到一个 Promise 对象)
javascript
PromiseConstructor.resolve<string>(value: string): Promise<string>
js
// 如果传入的参数为非 Promise 类型的对象,则返回的结果为成功的 Promise 对象
let p1 = Promise.resolve(521);
console.log(p1); // Promise { 521 }
p1.then(
value => console.log(value),
);
// 如果传入的参数为 Promise 对象,则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve("OK");
}));
p2.then(
value => console.log(value),
);
let p3 = Promise.resolve(new Promise((resolve, reject) => {
reject("ERROR");
}));
p3.catch(
reason => console.log(reason),
);
reject
这个方法也是属于 Promise
函数对象,并不属于实例对象
快速返回一个失败的 Promise 对象
javascript
PromiseConstructor.reject<never>(reason?: any): Promise<never>
html
<body>
<script>
// reject.js
let p1 = Promise.reject(521);
console.log(p1);
p1.catch(
reason => console.log(reason),
);
let p2 = Promise.reject(new Promise((resolve, reject) => {
resolve("OK"); // fullfilled -> rejected
}));
console.log(p2); // 一个失败的 Promise 对象,但是它的 PromiseResult 是上面 resolve("OK") 的成功的 Promise 对象
p2.catch(
reason => console.log(reason),
);
</script>
</body>
all
javascript
Promise.all(promises) // promises 是一个包含 n 个 promise 的数组
返回一个新的 Promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败
成功的结果是所有成功的 promises 结果组成的数组
失败的结果是 promises 数组中失败的 promise 的结果
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let p1 = new Promise((resolve, reject) => {
resolve("OK");
});
let p2 = Promise.resolve("Success");
let p3 = Promise.resolve("Yes");
const result = Promise.all([p1, p2, p3]);
console.log(result);
</script>
</body>
</html>
race
javascript
Promise.race(promises) // promises 是一个包含 n 个 promise 的数组
返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let p1 = new Promise((resolve, reject) => {
resolve("OK");
});
let p2 = Promise.resolve("Success");
let p3 = Promise.resolve("Yes");
console.log(Promise.race([p1, p2, p3])); // p1: OK
</script>
</body>
</html>
Promise 关键问题
如何修改 Promise 对象的状态
以下3种方式可以修改 Promise 对象的状态:
javascript
let p = new Promise((resolve, reject) => {
// resolve("ok"); // pending => fulfilled
// reject("error"); // pending => rejected
// throw "err"; // pending => rejected
});
能否执行多个回调
一个 promise 指定多个成功/失败回调时,当它的状态改变,都会得到调用
javascript
let p = new Promise((resolve, reject) => {
resolve("OK");
});
// 下面2个回调在 p 的状态改变时都会执行
p.then(value1 => console.log("value1"));
p.then(value2 => console.log("value2"));
改变 promise 状态和指定回调函数谁先谁后
都有可能:正常情况下是先指定回调 再改变状态(比如 fetch api),但也可以先改变状态 再指定回调(在执行器中直接同步调用 resolve/reject,然后再调用 then)
何时才能得到数据(回调何时执行):
如果先指定回调,那么当状态发生改变时,回调函数就会调用,得到数据
如果先改变状态,那么当指定回调时,回调函数就会被调用,得到数据
then 方法返回的新的 promise 的结果的状态由什么决定
由 then 指定的回调函数的执行结果决定:
- 如果抛出异常,新 promise 变为 rejected
- 如果返回非 promise 的任何值,新的 promise 变为 fulfilled,value 为返回的值
- 如果返回的是一个新的 promise,此 promise 的结果就会成为新的 promise 结果
javascript
let p4 = p.then(
value => {
console.log(value);
// throw "err"; // 新的 promise 变为 rejected
// return 22; // 新的 promise 变为 fulfilled
// return new Promise((resolve, reject) => {
// reject("error");
// }); // 此promise 就是新的 promise 结果
},
reason => console.warn(reason)
);
Promise 如何串连多个操作任务
promise 的 then() 返回一个新的 promise,可以展开成 then 的链式调用,通过 then 的链式调用串联多个同步/异步任务
javascript
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => console.log(value));
如果继续在后面调用 then 并打印 value,将会输出 undefined,因为上面的最后一个 then 没有返回值,那么就返回了 undefined
异常穿透
当使用 promise 的 then 链式调用时,可以在最后指定失败的回调,前面的任何操作出现了异常,都会传到最后失败的回调中处理
javascript
let p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("OK");
reject("err");
}, 1000);
});
p.then(value => console.log(111))
.then(value => console.log(222))
.then(value => console.log(333))
.catch(reason => console.warn(reason));
中断 promise 调用链
中断:当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
方法:在回调函数中返回一个 pendding 状态的 promise 对象
手撕 Promise
promise 的参数是 function executor(resolve, reject) {}
,其中两个形参 resolve
和 reject
作为两个钩子函数在 promise 中实现
javascript
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
const self = this; // 预先保存 this,因为下面函数的 this 指向 window
function resolve(data) {
if (self.PromiseState !== 'pending') return; // 状态只能改变一次
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled';
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data;
}
function reject(data) {
if (self.PromiseState !== 'pending') return;
self.PromiseState = 'rejected';
self.PromiseResult = data;
}
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {}
let p = new MyPromise((resolve, reject) => {
// resolve("OK");
throw "fuck";
});
console.log(p);
then 方法执行回调
javascript
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
}
异步任务回调的执行
上面 then 函数实现的问题在于,它是同步执行的。比如一个 pending 状态的 promise 调用 then 后不会产生任何效果,因为没有对应的处理逻辑
回想一下,异步任务被包装在了 executor()
中,分别由 resolve/reject 在不同的情况下执行(改变状态,设置结果)
所以需要在 then 中指定保存回调函数,然后在 resolve/reject 中判空并执行异步任务
javascript
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callback = {};
const self = this; // 预先保存 this,因为下面函数的 this 指向 window
function resolve(data) {
if (self.PromiseState !== 'pending') return; // 状态只能改变一次
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled';
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data;
self.callback.onResolved && self.callback.onResolved(data); // 执行回调
}
function reject(data) {
if (self.PromiseState !== 'pending') return;
self.PromiseState = 'rejected';
self.PromiseResult = data;
self.callback.onRejected && self.callback.onRejected(data);
}
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') { // 保存回调
this.callback = {
onResolved, onRejected
}
}
}
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
// resolve("OK")
reject("err");
}, 1000);
// throw "fuck";
});
console.log(p);
p.then(value => console.log(value), reason => console.warn(reason));
指定多个回调
只需要将 callback 对象变为数组,每次调用 then 都向 callbacks 中压入 2 个回调,同时在 resolve 和 reject 中执行所有回调
javascript
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
const self = this; // 预先保存 this,因为下面函数的 this 指向 window
function resolve(data) {
if (self.PromiseState !== 'pending') return; // 状态只能改变一次
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled';
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data;
self.callbacks.forEach(cb => cb.onResolved(data)); // 执行回调
}
function reject(data) {
if (self.PromiseState !== 'pending') return;
self.PromiseState = 'rejected';
self.PromiseResult = data;
self.callbacks.forEach(cb => cb.onRejected(data));
}
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') { // 保存回调
this.callbacks.push({
onResolved, onRejected
});
}
}
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("OK")
// reject("err");
}, 1000);
// throw "fuck";
});
console.log(p);
p.then(value => console.log(value), reason => console.warn(reason));
p.then(value => console.warn(value), reason => console.error(reason));
同步修改状态 then 方法返回 Promise
上面的 then 方法返回的是 undefined,而实际上 then 方法应该永远返回一个 promise
如果 onResolved 返回一个非 promise 对象,则将其包裹为一个 fulfilled 的 promise 返回
如果 onResolved 返回的是一个 promise 对象,则 then 返回的 promise 状态与之保持一致
javascript
MyPromise.prototype.then = function (onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
try {
// then 方法"成功回调"的执行结果
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result);
}
} catch(e) {
reject(e);
}
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') { // 保存回调
this.callbacks.push({
onResolved, onRejected
});
}
});
}
let p = new MyPromise((resolve, reject) => {
resolve("OK");
});
console.log(p);
let res = p.then(value => console.log(value), reason => console.warn(reason));
console.log(res); // -> promise
异步修改状态 then 方法返回 Promise
上面的 then 方法代码只能对已经处于 fulfilled 状态的 promise 进行处理(获取 onResolved 的返回值)
而对于一个处于 pending 状态的 promise 调用 then 后,它虽然保存了回调,但是它保存的只是用户指定的原始的 onResolved/onRejected,并没有包裹在 promise 中,所以我们需要对其进行类似的异步化处理
javascript
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
return new MyPromise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
try {
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result);
}
} catch(e) {
reject(e);
}
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') { // 保存回调
this.callbacks.push({
onResolved: function() {
try {
let result = onResolved(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result); // 改变当前 promise 状态
}
} catch(e) {
reject(e);
}
},
onRejected: function() {
try {
let result = onRejected(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
});
}
});
}
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
});
console.log(p);
let res = p.then(value => console.log(value), reason => console.warn(reason));
console.log(res); // -> promise
then 方法完善与优化
抽取重复函数进行封装:将 onResolved 和 onRejected 函数参数化为 type 即可
javascript
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
return new MyPromise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result);
}
} catch(e) {
reject(e);
}
}
if (this.PromiseState === 'fulfilled') {
callback(onResolved);
}
if (this.PromiseState === 'rejected') {
callback(onRejected);
}
if (this.PromiseState === 'pending') { // 保存回调
this.callbacks.push({
onResolved: function() {callback(onResolved)},
onRejected: function() {callback(onRejected)}
});
}
});
}
实现 catch 方法
javascript
MyPromise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
}
catch 方法可以直接复用 then 进行实现:只传 onRejectd,不传 onResolved
为了实现异常穿透,需要保证 then 方法可以只接受一个参数,所以需要给 onReject 赋予默认值
then 方法甚至允许不接受参数,直接向前推进,所以需要考虑给 onResolved 也赋予默认值
javascript
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason // 如果不定义 onReject,直接抛异常,层层传递,实现链式调用
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new MyPromise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result);
}
} catch(e) {
reject(e);
}
}
if (this.PromiseState === 'fulfilled') {
callback(onResolved);
}
if (this.PromiseState === 'rejected') {
callback(onRejected);
}
if (this.PromiseState === 'pending') { // 保存回调
this.callbacks.push({
onResolved: function() {callback(onResolved)},
onRejected: function() {callback(onRejected)}
});
}
});
}
let p = new MyPromise((resolve, reject) => {
// setTimeout(() => {
// resolve("OK");
// }, 1000);
reject("err");
});
MyPromise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
}
let res = p.then(v => console.log(111))
.then(v => console.log(222))
.catch(reason => console.warn(reason));
console.log(res); // -> promise
实现 resolve 方法
javascript
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(v => resolve(v), r => reject(r));
} else {
resolve(value);
}
});
}
// let p = MyPromise.resolve("hhh");
let p = MyPromise.resolve(new MyPromise((resolve, reject) => {
reject("error");
}));
console.log(p);
p.then(value => console.log(value)).catch(reason => console.warn(reason));
实现 reject 方法
javascript
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
let p = MyPromise.reject("hhh");
// let p = MyPromise.reject(new MyPromise((resolve, reject) => {
// resolve("error");
// }));
console.log(p);
p.then(value => console.log(value)).catch(reason => console.warn(reason));
实现 all 方法
javascript
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
let count = 0;
let values = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then( v => {
count++;
values[i] = v;
if (count === promises.length) {
resolve(values);
}
}, r => {
reject(r);
});
}
});
}
let p1 = MyPromise.resolve("hhh");
let p2 = MyPromise.resolve(new MyPromise((resolve, reject) => {
resolve("HHH");
}));
MyPromise.all([p1, p2]).then(value => console.log(value), reason => console.warn(reason));
实现 race 方法
javascript
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => resolve(v), r => reject(r));
}
});
}
let p2 = MyPromise.resolve(new MyPromise((resolve, reject) => {
resolve("HHH");
}));
let p1 = MyPromise.resolve("hhh");
MyPromise.race([p1, p2]).then(value => console.log(value), reason => console.warn(reason));
then 方法回调异步执行
通过 settimeout 使得操作异步执行
javascript
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
const self = this; // 预先保存 this,因为下面函数的 this 指向 window
function resolve(data) {
if (self.PromiseState !== 'pending') return; // 状态只能改变一次
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled';
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data;
setTimeout(() => {
self.callbacks.forEach(cb => cb.onResolved(data)); // 执行回调
});
}
function reject(data) {
if (self.PromiseState !== 'pending') return;
self.PromiseState = 'rejected';
self.PromiseResult = data;
setTimeout(() => {
self.callbacks.forEach(cb => cb.onRejected(data));
});
}
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
MyPromise.prototype.then = function(onResolved, onRejected) {
const self = this;
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new MyPromise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result);
}
} catch(e) {
reject(e);
}
}
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved);
});
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
});
}
if (this.PromiseState === 'pending') { // 保存回调
this.callbacks.push({
onResolved: function() {callback(onResolved)},
onRejected: function() {callback(onRejected)}
});
}
});
}
class 版本实现
javascript
class MyPromise {
constructor (executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
const self = this; // 预先保存 this,因为下面函数的 this 指向 window
function resolve(data) {
if (self.PromiseState !== 'pending') return; // 状态只能改变一次
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled';
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data;
setTimeout(() => {
self.callbacks.forEach(cb => cb.onResolved(data)); // 执行回调
});
}
function reject(data) {
if (self.PromiseState !== 'pending') return;
self.PromiseState = 'rejected';
self.PromiseResult = data;
setTimeout(() => {
self.callbacks.forEach(cb => cb.onRejected(data));
});
}
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
then(onResolved, onRejected) {
const self = this;
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new MyPromise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(v => resolve(v), r => reject(r));
} else {
resolve(result);
}
} catch(e) {
reject(e);
}
}
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved);
});
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
});
}
if (this.PromiseState === 'pending') { // 保存回调
this.callbacks.push({
onResolved: function() {callback(onResolved)},
onRejected: function() {callback(onRejected)}
});
}
});
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(v => resolve(v), r => reject(r));
} else {
resolve(value);
}
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
static all(promises) {
return new MyPromise((resolve, reject) => {
let count = 0;
let values = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then( v => {
count++;
values[i] = v;
if (count === promises.length) {
resolve(values);
}
}, r => {
reject(r);
});
}
});
}
static race(promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v);
}, r => {
reject(r);
});
}
});
}
}
async 与 await
async 函数
如果返回的是一个非 promise 的结果,则相当于返回 Promise.resolve()
如果返回的是一个 promise 对象,则相当于返回这个 promise 对象本身
如果抛出异常,则返回一个 rejected 的 promise,PromiseResult 设置为异常原因
await 表达式
await 右侧的表达式一般为 promise 对象,但是也可以是其他的值
如果表达式是 promise 对象,await 返回的是 promise 成功的值。如果 promise 发生了异常,则需要使用 try-catch 进行处理
如果表达式是其他值,则直接将此值本身作为 await 的返回值
宏队列和微队列
js 的执行引擎基于事件循环,只有将所有的同步代码都执行完成,才会执行队列中的回调任务
分析下面代码的执行输出(顺序)
javascript
setTimeout(() => {
console.log("marco queue");
}, 0);
Promise.resolve(1).then(value => console.log("micro queue"));
虽然微队列的任务是后放入的,但是他具有较高的优先级,故优先执行 promise 中的回调,然后执行定时器中的回调
sh
micro queue
marco queue
只要微队列中还有任务,就必须先执行:每次取出第一个宏任务执行前,都要将所有的微任务一个一个取出来执行
javascript
setTimeout(() => {
console.log(2);
Promise.resolve(3).then(value => console.log(value)); // 在执行宏队列中的任务时,向微队列插入任务
}, 0);
setTimeout(() => {
console.log(4)
}, 0);
Promise.resolve(1).then(value => console.log(value));
Promise.resolve(5).then(value => console.log(value));
javascript
1
5
2
3
4
面试题
分析下面代码的执行循序:
javascript
setTimeout(() => { // 压入宏队列
console.log(1)
}, 0);
Promise.resolve().then(() => { // 压入微队列
console.log(2);
});
Promise.resolve().then(() => { // 压入微队列
console.log(4);
});
console.log(3); // 同步执行
执行结果:
javascript
3
2
4
1
分析下面代码的执行顺序:
javascript
setTimeout(() => { // 压入宏队列
console.log(1);
}, 0);
new Promise((resolve) => {
console.log(2); // 同步执行
resolve();
}).then(() => { // 压入微队列
console.log(3);
}).then(() => { // 压入微队列
console.log(4);
});
console.log(5); // 同步执行
执行结果:
javascript
2
5
3
4
1
分析下面代码的执行顺序:
javascript
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
}, 0);
resolve(1);
});
resolve(2);
p.then(value => console.log(value));
}));
first().then(value => console.log(value));
console.log(4);
首先同步代码输出 374
,因为 p 调用 then 的时候已经是 fulfilled 状态,所以直接将其压入微队列。同理,first()
也返回一个 fulfilled 状态的 promise,所以直接将这个异步任务压入微队列
然后取出微队列中的2个异步任务执行,输出 12
。最后输出宏队列中的异步任务 5
javascript
3
7
4
1
2
5
分析下面代码的执行顺序:
javascript
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);
});
首先将定时器任务压入宏队列,然后同步执行第一个 promise 的 executor 函数,输出 1
,因为他立即成功,所以将 then 中的"输出2的任务"压入微队列
接着同步执行第二个 promise 的 executor 函数,输出 7
,因为他立即成功,所以将"输出8的任务"压入微队列
然后从微队列取出"输出2的任务",执行并输出 2
,接着又 new 了一个 promise,同步执行它的 executor,输出 3
,紧接着这个 promise 成功,所以将"输出4的任务"压入微队列,此时这个 promise 的 executor 已经执行完毕,所以将"输出6的任务"压入微队列
然后从微队列取出"输出8的任务",执行并输出 8
然后从微队列取出"输出4的任务",执行并输出 4
,并且把"输出5的任务"压入微队列
然后从微队列中取出"输出6的任务",执行并输出 6
然后从微队列中取出"输出5的任务",执行并输出 5
最后取出宏队列中的任务,输出 0
javascript
1
7
2
3
8
4
6
5
0