跳到主要内容

浏览器的常驻线程

JS引擎线程负责解释和执行JavaScript代码。

GUI线程用于绘制用户界面,其执行与JS主线程是互斥的。

HTTP网络请求线程处理网络请求,收到响应后将回调函数推入任务队列。

定时器触发线程负责setTimeoutsetInterval,等待时间结束后将执行函数推入任务队列。

浏览器事件处理线程处理诸如clickmouse等用户交互事件,事件发生后将其放入事件队列中。

任务优先级

同步与异步

同步任务的优先级高于异步任务。异步任务又分为微任务宏任务

宏任务

setTimeout

setTimeout(function () {
console.log('timeout');
}, 0); // 实际上为4ms
console.log('hi');

微任务

微任务由Promise产生。

Promise具有三种状态:pending(进行中)、fulfilled(已完成)和rejected(以失败)。Promise的状态不受外界影响。

Promise经历两个阶段:从进行中到完成,以及从进行中到失败。

const fs = require('fs');

function readFile(pathname) {
return new Promise((resolve, reject) => {
console.log(1);
fs.readFile(pathname, 'utf-8', (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}

const promise = readFile('./name.txt');

promise.then(
(data) => {
console.log(data);
},
(err) => {
console.log(err);
}
);

Promise 的固化

一旦Promise的状态确定,不会再改变。

const p1 = new Promise((resolve, reject) => {
resolve(1);
reject(10);
});

p1.then((res) => console.log(res)).catch((err) => console.log(err));

.then 方法

then方法是异步的。

new Promise((resolve, reject) => {
// resolve("一瓶可乐");
reject('买不到');
}).then(
(resolve) => {
console.log(resolve);
},
(reject) => {
console.log(reject);
// 输出: 买不到
}
);

为什么要有then方法

有时需要一步步完成任务,完成第一步后才能进行第二步,then方法可以逐步完成任务。同时,then方法用于处理Promise的成功或失败。

const p1 = new Promise((resolve, reject) => {
resolve('fulfilled');
})
.then(
(value) => {
return '成功';
},
(reason) => console.log('error' + reason)
)
.then((value) => console.log('成功2'));

then 包装

then方法在类中会自动包装成一个Promise

class Person {
constructor() {}
then(resolve, reject) {
resolve();
}
}

async function get() {
await new Person();
console.log('6666');
}

get();

执行顺序

执行顺序为同步任务优先,其次是微任务,最后是宏任务。

setTimeout(() => {
console.log('setTimeout-宏任务');
}, 0);

new Promise((resolve) => {
resolve();
console.log('Promise-内的console-同步任务');
}).then((value) => console.log('then-成功-微任务'));

console.log('最外层-console-同步任务');

宏任务内执行微任务

宏任务执行完毕后,才会开始执行微任务。

new Promise((resolve) => {
setTimeout(() => {
console.log('setTimeout-宏任务');
resolve();
}, 0);
console.log('Promise-内的console-同步任务');
}).then((value) => console.log('then-成功-微任务'));

console.log('最外层-console-同步任务');

只关注失败的回调函数,简单的写法

const p1 = Promise.resolve(1);
const p2 = Promise.reject(2);

p1.then((data) => {
console.log(data);
});

// 使用.catch方法只关注失败的回调函数
p2.catch((err) => {
console.log('err' + err);
});

Promise 的检验

thenable主要用于检验一个对象是否是Promise。判断一个对象是否为Promise,只需检查其是否具有.then方法。

Thenable 对象

第三章: Promise - Thenable 鸭子类型(Duck Typing) - 《你不懂 JS: 异步与性能(You Dont Know JS)(第一版)》 - 书栈网 · BookStack

const obj = {
then(resolve, reject) {
resolve(11);
},
};

const p1 = Promise.resolve(obj);

p1.then((data) => {
console.log(data); // 输出: 11
});

finally

finally方法在Promise结束后,无论成功还是失败,都会执行相应的动作。

异步任务与微任务

微任务的优先级高于异步任务。

// 微任务
Promise.resolve().then(() => {
console.log('promise1'); // 输出: promise1
setTimeout(() => {
console.log('setTimeout2'); // 输出: setTimeout2
}, 0);
});

// 异步任务
setTimeout(() => {
console.log('setTimeout1'); // 输出: setTimeout1
Promise.resolve().then(() => {
console.log('promise2'); // 输出: promise2
});
}, 0);

同步、异步、微任务

const p1 = new Promise((resolve, reject) => {
resolve('fulfilled');
});

// then 返回一个Promise对象
const p2 = p1.then(
(val) => console.log(val),
(res) => console.log(res)
);

// 同步任务直接打印
console.log(p1); // 输出: Promise {<fulfilled>: 'fulfilled'}
console.log(p2); // 输出: Promise {<pending>}

链式调用

Promise在链式调用时会返回普通值或Promise对象。

执行三次

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 1000);
});

p1.then((res) => {
console.log(res + 1);
return res + 1;
});

p1.then((res) => {
console.log(res + 1);
return res + 1;
});

p1.then((res) => {
console.log(res + 1);
return res + 1;
});

// 输出: 11 11 11

链式调用三个 Promise

链式调用会创建三个Promise,每个Promise对应一个状态。

p1.then((res) => {
console.log(res + 1);
return res + 1;
})
.then((res) => {
console.log(res + 1);
return res + 1;
})
.then((res) => {
console.log(res + 1);
return res + 1;
});

// 输出: 11 12 13

多层嵌套

const p1 = new Promise((resolve, reject) => {
resolve(1);
reject(10);
});

p1.then((res) => console.log(res)) // 输出: 1
.then() // 忽略
.then() // 忽略
.then((res) => console.log(res)) // 输出: undefined
.catch((err) => console.log(err));

状态依赖

如果Promise的状态存在依赖,依赖的Promise状态将决定自身的状态。

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('10000');
}, 3000);
});

const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(p1);
}, 1000);
// p2不会再等待1秒,会直接执行p1的结果
});

p2.then((res) => console.log(res)).catch((err) => console.log(err));

Promise.all

Promise.all在一次拿到所有Promise的结果后返回结果。如果其中有一个Promise失败,则返回错误。

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 1000);
});

const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(20);
}, 2000);
});

const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(30);
}, 3000);
});

const p4 = Promise.all([p1, p2, p3]);

p4.then((res) => {
console.log(res); // 输出: [10, 20, 30]
});

Promise.race

Promise.race返回最快完成的Promise的结果,无论是成功还是失败。

const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 1000);
});

const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(20);
}, 2000);
});

const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(30);
}, 3000);
});

const p4 = Promise.race([p1, p2, p3]);

p4.then(
(res) => {
console.log(res); // 输出: 10
},
(err) => {
console.log(err + 'err');
}
);

Promise.allSettled()

当有多个彼此不依赖的异步任务完成时,或者需要知道每个Promise的结果,通常使用Promise.allSettled

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));

Promise.allSettled([promise1, promise2]).then((results) => results.forEach((result) => console.log(result.status)));

Node 读取文件的 Promise 示例

const fs = require('fs');

function readFile(pathname) {
return new Promise((resolve, reject) => {
fs.readFile(pathname, 'utf-8', (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}

readFile('./name.txt')
.then((res) => readFile(res))
.then((res) => readFile(res))
.then((res) => console.log(res)); // 输出: 100