回调函数(Callback)、事件监听、发布订阅、Promise/A+、生成器Generators/ yield、async/await
async/await函数对 Generator 函数的改进,体现在以下三点:
在执行任务队列中任务,包括宏任务和微任务。
宏任务 Macrotask
宏任务是指Event Loop在每个阶段执行的任务
微任务 Microtask
微任务是指Event Loop在每个阶段之间执行的任务
在node V8中,这两种类型的真实任务顺序如下所示:
宏任务 Macrotask队列真实包含任务:
script(主程序代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
注意:setTimeout 如果设置了大于0的延时,就会在 setImmediate后面执行。
微任务 Microtask队列真实包含任务:
process.nextTick, Promises, Object.observe, MutationObserver
PS:先执行主线程中的同步任务。再按照宏任务和微任务的顺序执行,一个await()、then()、catch() 都加一级,碰到其他宏任务,就按序添加在其他宏任务后面。从一级到最后一级,从上到下按序运行。
例子:
- console.time('start');
- setTimeout(function() {
- console.log(2);
- }, 10);
- setImmediate(function() {
- console.log(1);
- });
- new Promise(function(resolve) {
- console.log(3);
- resolve();
- console.log(4);
- }).then(function() {
- console.log(5);
- console.timeEnd('start')
- });
- console.log(6);
- process.nextTick(function() {
- console.log(7);
- });
- console.log(8);
-
- // 综合的执行顺序就是: 3——>4——>6——>8——>7——>5——>start: 7.009ms——>1——>2。
浏览器和 Node 环境下,microtask 任务队列的执行时机不同
(参考 帖子:https://blog.csdn.net/Fundebug/article/details/86487117)
var p = Promise.all([p1, p2, p3]);
Promise.all
方法接受一个数组作为参数,p1
、p2
、p3
都是 Promise
实例,如果不是,就会先调用下面讲到的Promise.resolve
方法,将参数转为 Promise
实例,再进一步处理。(Promise.all
方法的参数可以不是数组,但必须具有 Iterator
接口,且返回的每个成员都是 Promise
实例。)p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1) 只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2) 只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
var p = Promise.race([p1, p2, p3]);
Promise.race
方法同样是将多个Promise
实例,包装成一个新的Promise
实例。只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise
实例的返回值,就传递给p
的回调函数。Promise.race
方法的参数与Promise.all
方法一样,如果不是 Promise
实例,就会先调用下面讲到的Promise.resolve
方法,将参数转为Promise
实例,再进一步处理。
- Promise.resolve(value) ;
- // 等价于
- new Promise(resolve => resolve(value));
-
- var p = Promise.reject(reason);
- // 等同于
- var p = new Promise((resolve, reject) => reject(reason));
Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数。这一点与Promise.resolve
方法不一致。
- const thenable = {
- then(resolve) {
- resolve('ok');
- }
- };
-
- Promise.resolve(thenable)
- .then(e => {
- console.log(e === 'ok'); //true
- });
-
- Promise.reject(thenable)
- .catch(e => {
- console.log(e === thenable); // true
- });
- const fs = require('fs');
- const path = require('path');
-
- function asyncGetFile(p){
- return new Promise(function(resolve, reject){
- fs.readFile(path.join(__dirname, p), 'utf-8', (err, data) => {
- if (err) reject(err);
- else resolve(data);
- })
- })
- }
- asyncGetFile('./some.txt')
- .then(data => {……})
- .catch(err => {……});
-
用js实现sleep,用promise
- function sleep(time){
- return new Promise(resolve => setTimeout(resolve, time))
- }
- sleep(2000).then(()=>{……})
- // 这种方式实际上是用了 setTimeout,没有形成进程阻塞,不会造成性能和负载问题。
(??此点好好思考)
- class Scheduler {
- constructor() {
- this.tasks = [], // 待运行的任务
- this.usingTask = [] // 正在运行的任务
- }
- // promiseCreator 是一个异步函数,return Promise
- add(promiseCreator) {
- return new Promise((resolve, reject) => {
- promiseCreator.resolve = resolve
- if (this.usingTask.length < 2) {
- this.usingRun(promiseCreator)
- } else {
- this.tasks.push(promiseCreator)
- }
- })
- }
-
- usingRun(promiseCreator) {
- this.usingTask.push(promiseCreator)
- promiseCreator().then(() => {
- promiseCreator.resolve()
- this.usingMove(promiseCreator)
- if (this.tasks.length > 0) {
- this.usingRun(this.tasks.shift())
- }
- })
- }
-
- usingMove(promiseCreator) {
- let index = this.usingTask.findIndex(promiseCreator)
- this.usingTask.splice(index, 1)
- }
- }
-
- const timeout = (time) => new Promise(resolve => {
- setTimeout(resolve, time)
- })
-
- const scheduler = new Scheduler()
-
- const addTask = (time, order) => {
- scheduler.add(() => timeout(time)).then(() => console.log(order))
- }
-
- addTask(400, 4)
- addTask(200, 2)
- addTask(300, 3)
(学习来源:牛客网)
1、不得不说,异步这个功能在程序的世界里,都是拔尖的复杂。还有一个也挺复杂的:多线程,这让我想起了大学学习java的时候,Thread多线程,有两个线程 A和B,其中A先调用加一,B再调用减一……