web高级7期(三)

339 阅读3分钟

EventLoop事件循环

同步异步编程

/*
 * 进程和线程
 *   + 进程:可以理解为一个程序(浏览器打开一个页面就是开辟一个进程) 
 *   + 线程:程序中具体干事的人
 *   一个进程中可以包含很多的线程,一个线程同时只能做一件事情
 * 
 * 同步编程:一件事一件事的去做,上一件事情没有完成,则无法处理下一个事情「单线程」
 * 异步编程:上一件事情没有处理完成,则下一件事情可以继续去处理「多线程、基于单线程的EventLoop机制...」
 * 
 * JS中的异步编程
 *   「异步微任务」
 *    + promise
 *    + async/await 「generator」
 *    + requestAnimationFrame
 *   「异步宏任务」
 *    + 定时器
 *    + ajax「HTTP网络请求」
 *    + 事件绑定
 *    + ...
 * 
 * JS是单线程的,所以:
 *   1. JS中大部分代码都是同步编程
 *   2. 但是可以基于单线程的EventLoop(事件循环机制)实现出异步的效果
 * 
 * 浏览器是多线程的,打开一个页面,浏览器会分配很多线程,同时处理一些事情
 *   + GUI渲染线程:自上而下渲染页面 
 *   + JS引擎(渲染)线程:JS单线程是因为,浏览器只会开辟这一个线程,用来执行JS代码
 *   + HTTP网络请求线程:加载资源文件还是一些数据
 *   + 定时器监听线程:监听定时器是否到达时间
 *   + DOM事件监听线程:监听DOM事件的触发
 *   + ...
 */

image.png

image.png

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
console.log(4);
// console.time('AA');
for (let i = 0; i < 90000000; i++) {
    // do soming
}
// console.timeEnd('AA'); //=>AA: 79ms 左右
console.log(5);
setTimeout(() => {
    console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
    console.log(8);
}, 15);
console.log(9);

image.png

setTimeout(() => {
    console.log(1);
}, 0);
console.log(2);
while (1) {
    // do somthing
}
console.log(3);
setTimeout(() => {
    console.log(4);
}, 10);
console.log(5);

//========================
setTimeout(function () {
    console.log(1);
}, 0); //异步宏任务  5ms  function->1  //=>1
console.log(2); //=>2
// throw new Error('xxx');
console.log(a); //报错  Uncaught ReferenceError: a is not defined
console.log(3);
setTimeout(() => {
    console.log(4);
}, 10);

setTimeout(function () {
    console.log(1);
}, 0); //异步宏任务1  5ms  function->1 
console.log(2);

try {
    console.log(a);
} catch (err) {}

console.log(3);
setTimeout(() => {
    console.log(4);
}, 10); //异步宏任务1  10ms  function->4

image.png

promise基础语法

/*
 * ES6中新增一个内置类:Promise 承诺/约定模式,基于这种模式可以有效处理异步编程,可以解决异步编程中产生的”回调地狱“
 */
// 需求:首先从服务器端基于"/api/1"这个接口获取数据,紧接着把获取的某些数据作为参数,再基于"/api/2"接口获取其他数据;最后基于第二次请求的数据作为参数,再请求"/api/3"=>ajax串行
// 传统在实现异步操作,并且是串行的模式下,基本上都是回调函数嵌套回调函数,实现非常的恶心 ”回调地狱“
$.ajax({
    url: "/api/1",
    dataType: "json",
    success(result){
        
        $.ajax({
            url: "/api/2",
            dataType: "json",
            success(result){
                
                $.ajax({
                    url: "/api/3",
                    dataType: "json",
                    success(result){

                    }
                })
                
            }
        })
        
    }
})

设计模式就是按照一定思想有效管理代码,让代码可读性更高,维护性更强

// let p1 = new Promise(); //Uncaught TypeError: Promise resolver undefined is not a function 要求new的时候,必须传递一个函数才可以[executor]

// new Promise
//   - 会立即执行传递的executor函数
//       - 在executor函数中一般用来管控一个异步的操作(不写异步也可以)
//       - 而且传递给executor函数两个参数:resolve reject,并且这两个参数都是函数
//   - 会创建Promise类的一个实例p1
//       - [[PromiseState]]promise状态:pending准备状态、fulfilled/resolved成功(兑现)、rejected失败(已拒绝)
//       - [[PromiseResult]]promise值:默认是undefined,一般存储成功的结果或者失败的原因
//       - p1.__proto__ -> Promise.prototype: then/catch/finally

let p1 = new Promise(function(resolve, reject){
    // 执行resolve控制实例的状态为成功,传递的100是成功的结果
    // resolve(100)
    // 执行reject控制实例的状态为失败,传递的100是失败的原因
    // reject(100)
    // 如果executor代码执行报错,则实例的状态也会变为失败,并且实例的值是报错的原因
    // 一但状态从pending变为resolved或者rejected,都无法再次改变其状态
})
// Promise是如何管控异步编程的?
//  - new Promise的时候创建一个promise实例,此时在executor函数中管理一套异步的代码
//  - 后期等异步操作成功或者失败的时候,执行resolve/reject,以此来控制promise实例的状态和结果
//  - 根据状态和结果,就可以控制基于.then注入的两个方法中的哪一个去执行
// 下述代码执行的顺序
//  1. new Promise
//  2. 执行executor:设置一个定时器
//  3. then注入两个方法(注入的方法保存起来)
//  -----等待1000ms
//  4. 执行定时器的回调函数:执行resolve改变promise状态和值
//  5. 通知之前基于then注入的两个方法中的第一个执行

let p1 = new Promise(function(resolve, reject){
    // new Promise的时候立即执行executor函数,在executor函数中管理了一个异步编程代码[此时状态是pending];当异步操作到达指定时间,开始执行的时候(理解为异步操作成功),此时我们通过resolve,把promise状态修改为resolved
    setTimeout(() => {
        resolve('ok')
    }, 1000)
})
p1.then(result => {
    // 当p1实例的状态修改为resolved的时候,通知传递的第一个函数执行,result->[[PromiseResult]]
    console.log('成功', result)
}, reason => {
    // 当p1实例的状态修改为rejected的时候,通知传递的第二个函数执行,reason->[[PromiseResult]]
    console.log('失败', reason)
})
let p1 = new Promise(function(resolve, reject){
    console.log(1) // -> 1
    resolve('ok') // 立即修改状态和值,并且通知基于then注入的方法执行(问题:then还没有执行,方法还没有注入,不知道该通知谁来执行),所以此时需要把“通知方法执行的操作”先保存起来,(放到等待任务队列中),“通知方法执行”这个操作本身是异步的,需要等待方法注入完成后再通知其执行
    console.log(2) // -> 2
})
p1.then(result => {
    console.log('成功', result)  // -> 4
}, reason => {
    console.log('失败', reason) 
})
console.log(3) // -> 3
let p1 = new Promise(function(resolve, reject){
    setTimeout(()=>{
        resolve('ok')
        console.log(1) //->(1) 再次说明,不论是否基于then注入了方法,执行resolve/reject的时候“修改状态和值”是同步的(立即处理),但是“通知对应注入的方法执行”的这个任务就是异步操作的(不会立即处理,只是把它排到等待任务队列中,当其它事情处理完,再次返回头,通知对应注入的方法执行)
    }, 1000)
})
p1.then(result => {
    console.log(2)  // -> 4 //->(2)
}

//
/* let p1 = new Promise(function(resolve, reject){
    resolve('ok')
}) */
// Promise.resolve() 创建一个状态为成功的promise实例
// Promise.reject() 创建一个状态为失败的promise实例

/*
 * 执行.then方法返回一个全新的promise实例
 * promise实例状态和值的分析
 * 第一种情况:new Promise出来的实例
 *     - resolve/reject的执行控制其状态和值
 *     - executor函数执行失败控制其状态为失败,值为报错信息
 * 第二种情况:.then返回的新实例
 *     - then注入的两个方法,执行报错状态为失败,值为报错信息,不报错
 *     - 看then的返回值,返回值为promise实例,此实例的成功或失败,直接决定.then返回实例的成功和失败,返回值不为promise实例,返回状态为成功值为当前返回值的promise实例
 */
let p1 = new Promise(function(resolve, reject){
    resolve('ok')
})
let p2 = p1.then(result => {
    console.log('成功', result) // 成功 'ok'
    return 10
}, reason => {
    console.log('失败', reason)
})
p2.then(result => {
    console.log('成功', result) // 成功 10
}, reason => {
    console.log('失败', reason)
})


let p1 = new Promise(function(resolve, reject){
    reject('no')
})
let p2 = p1.then(result => {
    console.log('成功', result) 
}, reason => {
    console.log('失败', reason) // 失败 no
})
p2.then(result => {
    console.log('成功', result) // 成功 undefined
}, reason => {
    console.log('失败', reason)
})


let p1 = Promise.resolve('ok')
let p2 = p1.then(result => {
    console.log('成功', result) // 成功 'ok'
    return Promise.reject('no')
}, reason => {
    console.log('失败', reason)
})
p2.then(result => {
    console.log('成功', result)
}, reason => {
    console.log('失败', reason) // 失败 'no'
})

//对于失败的promise实例,如果没有编写方法处理结果,则会在控制台抛出异常信息(但是不会阻碍其余代码执行)
Promise.reject().then(result => {}, reason => {})

// then注入的方法没有传递,会顺延到下一个then中具备相同状态的处理函数上
Promise.reject('NO').then(/* result => result */, /* reason => Promise.reject(reason) */).then(reuslt => {
    console.log('成功', result)
},reason=>{
    console.log('失败', reason) //失败 'NO'
})

// 真实项目中,在多个then下,then处理的都是成功的情况,最后一个then存放失败的,这样不论是第一次还是某一次导致promise实例状态为失败的,最后都会顺延到最后一个失败的处理函数上进行处理
// - then(null, reason=>{...}) 用catch(reason=>{...})代替
Promise.reject('no').then(result=>{
    console.log('成功', result)
}).then(result=>{
    console.log('成功', result)
}).catch(reason=>{
    console.log('失败', reason)
})

Promise.resolve('ok').then(result=>{
    console.log('成功', result)
}).then(result=>{
    console.log('成功', result)
    return Promise.reject('no')
}).catch(reason=>{
    console.log('失败', reason)
})

// Promise.all() 等待所有的promise实例都成功,整体返回的状态才是成功,只要有一个失败,整体状态就是失败
// Promise.race() 看多个实例谁先处理完,先处理完成的状态(不论成功或者失败)就是最后整体的状态
let p = Promise.all([p1, p2, p3, p4])
p.then(results => {
    //都成功,p就是成功的:results是按照之前设定的顺序依次存储每一个promise的结果
}).catch(reason => {
   //只要处理过程中有一个失败的,则立即结束处理,p也是失败的:谁失败的,记录谁的失败原因
})

async await

// async await ES7新增的
// async:修饰函数,最后默认让函数返回一个promise实例(函数执行报错,实例状态就是失败,值为报错信息;否则实例状态为成功,结果是return的值)-> 一般都是配合await使用,(函数中使用await,则必须基于async修饰)
async function fn(){
    return 10
}
fn().then(result => {
    console.log(result)
})


// await promise实例:如果设置的不是promise实例
//    - 正常的值 await 10 -> await Promise.resolve(10)
//    - 函数执行 await xxx() -> 首先立即执行xxx函数,接收它的返回值 -> await 返回值 
// 本身是异步微任务:把当前上下文中await下面要执行的代码整体存储到异步的微任务中,当await后面的promise实例状态变为成功后,再去执行下面的代码(也就是那个异步的微任务)
function computed () {
    console.log(1) //->(3)
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(2)
        }, 1000)
    })
}
console.log(3) // ->(1)
async function fn () {
    console.log(4) // ->(2)
    let result = await computed()
    console.log(result) // ->(5)
    console.log(5) // ->(6)
}
fn()
console.log(6) // ->(4)


// 对失败的promise实例没有做异常的处理,则控制台抛出异常信息「不会影响后续代码执行」
//    + promise.catch(reason=>{})
//    + await需要自己基于try catch做异常捕获
async function fn() {
    try {
        let result = await Promise.reject(100);
        console.log(result);
    } catch (err) {}
}
fn();


// Uncaught RangeError: Maximum call stack size exceeded
let i = 0;
function fn() {
    console.log(++i);
    fn();
}
fn();

// 不会抱错,但是会形成死循环  =>在EventLoop机制中,只有主线程空闲才会执行异步的任务
function delay(interval = 500) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, interval);
    });
}

let i = 0;
async function fn() {
    console.log(++i);
    // await delay();
    await 0;
    fn();
}
fn();

同步异步和事件循环习题

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function () {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {
    console.log('promise1');
    resolve();
}).then(function () {
    console.log('promise2');
});
console.log('script end');

image.png

let body = document.body;
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(1);
    });
    console.log(2);
});
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(3);
    });
    console.log(4);
});

image.png

// => 'start'  'p1'  'p2'  'timeout1'  ‘p3’  ‘p4’  'interval'...
// 微任务1  微任务2  宏任务1  微任务3  微任务4  宏任务2...
console.log('start');
let intervalId;
Promise.resolve().then(() => { //微任务1 「找到微任务队列即可执行,因为此时promise实例已经是成功的」
    console.log('p1');
}).then(() => { //微任务2 「只有微任务1的onfulfilled方法执行完,没有报错,证明其promise是成功的,才会执行微任务2」
    console.log('p2');
});
setTimeout(() => { //宏任务1
    Promise.resolve().then(() => { //微任务3 「找到微任务队列即可执行」
        console.log('p3');
    }).then(() => { //微任务4 「微任务3执行完才可以执行」
        console.log('p4');
    });
    intervalId = setInterval(() => { //宏任务2
        console.log('interval');
    }, 3000);
    console.log('timeout1');
}, 0);

// p1 p3 p2
Promise.resolve().then(() => {
    console.log('p1');
}).then(() => { 
    console.log('p2');
});
Promise.resolve().then(() => {
    console.log('p3');
})

// => ‘b’ ‘f’ ‘c’ ‘a’ ‘d’
// 微任务1  微任务2  微任务4  微任务3  宏任务1  宏任务2
setTimeout(() => { //宏任务1 
    console.log('a');
});
Promise.resolve().then(() => { //微任务1 「进入到微任务队列即执行」
    console.log('b');
}).then(() => { //微任务2 「微任务1执行完即可」
    return Promise.resolve('c').then(data => { //微任务4 「进入到微任务队列立即执行」
        setTimeout(() => { //宏任务2
            console.log('d')
        });
        console.log('f');
        return data;
    });
}).then(data => { //微任务3 「微任务2执行完,并且告知其promise实例是成功的才可以执行」
    console.log(data);
});
function func1() {
    console.log('func1 start'); // 3 8
    return new Promise(resolve => {
        resolve('OK');
    });
}
function func2() {
    console.log('func2 start'); // 4
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('OK');
        }, 10);
    });
}
console.log(1); // 1
setTimeout(async () => {
    console.log(2); // 7
    await func1();
    console.log(3); // 9
}, 20);
for (let i = 0; i < 90000000; i++) {} //循环大约要进行80MS左右
console.log(4); // 2
func1().then(result => {
    console.log(5); // 6
});
func2().then(result => {
    console.log(6); // 11
});
setTimeout(() => {
    console.log(7); // 10
}, 0);
console.log(8); // 5

image.png

promise A+源码

/* 
 * 基于原生JS实现Promise「遵循的是Promise A Plus规范」
 *    https://promisesaplus.com/ 
 */
(function () {
    function Promise(executor) {
        // 要求传递的executor必须是一个函数才可以
        if (typeof executor !== 'function') throw new TypeError('Promise resolver ' + executor + ' is not a function');

        // self->promise实例 && 初始其状态state和值value
        var self = this;
        self.state = 'pending';
        self.value = undefined;
        self.onFulfilledCallbacks = [];
        self.onRejectedCallbacks = [];

        var change = function change(state, value) {
            // 状态只能修改一次,第二次更改无效「啥都不处理即可」
            if (self.state !== 'pending') return;
            self.state = state;
            self.value = value;
            
            // 通知集合中的方法执行
            setTimeout(function () {
                var callbacks = self.state === 'fulfilled' ? self.onFulfilledCallbacks : self.onRejectedCallbacks;
                for (var i = 0; i < callbacks.length; i++) {
                    var item = callbacks[i];
                    if (typeof item === "function") {
                        item(self.value);
                    }
                }
            });
        };

        try {
            executor(function resolve(result) {
                change('fulfilled', result);
            }, function reject(reason) {
                change('rejected', reason);
            });
        } catch (err) {
            change('rejected', err);
        }
    }

    Promise.prototype = {
        constructor: Promise,
        customize: true,
        /*
         * .then(onfulfilled, onrejected) 
         *    case1:如果此时promise实例已经是成功或者失败,我们创建一个异步的微任务,等待同步任务结束,执行对应的函数即可
         *    case2:此时状态还是pending,我们需要把onfulfilled/onrejected保存起来,当后期状态修改了(例如:resolve/reject方法执行),再次通知保存的方法执行,而这个操作也是异步微任务
         */
        then: function (onfulfilled, onrejected) {
            var self = this;
            switch (self.state) {
                case 'fulfilled':
                    setTimeout(function () {
                        onfulfilled(self.value);
                    });
                    break;
                case 'rejected':
                    setTimeout(function () {
                        onrejected(self.value);
                    });
                    break;
                default:
                    self.onFulfilledCallbacks.push(onfulfilled);
                    self.onRejectedCallbacks.push(onrejected);
            }
        },
        catch: function () {}
    };

    // 暴露API
    if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = Promise;
    }
    if (typeof window !== 'undefined') {
        window.Promise = Promise;
    }
})();

let p1 = new Promise((resolve, reject) => {
    // resolve(100);
    // reject(0);
    setTimeout(() => {
        resolve(100);
        console.log(2);
    }, 1000);
});
p1.then(result => {
    console.log('成功', result);
}, reason => {
    console.log('失败', reason);
});
p1.then(result => {
    console.log('成功', result);
}, reason => {
    console.log('失败', reason);
});
console.log(`1`);
/* 
 * 基于原生JS实现Promise「遵循的是Promise A Plus规范」
 *    https://promisesaplus.com/ 
 */
(function () {
    function Promise(executor) {
        if (typeof executor !== 'function') throw new TypeError('Promise resolver ' + executor + ' is not a function');

        var self = this;
        self.state = 'pending';
        self.value = undefined;
        self.onFulfilledCallbacks = [];
        self.onRejectedCallbacks = [];
        var change = function change(state, value) {
            if (self.state !== 'pending') return;
            self.state = state;
            self.value = value;
            setTimeout(function () {
                var callbacks = self.state === 'fulfilled' ? self.onFulfilledCallbacks : self.onRejectedCallbacks;
                for (var i = 0; i < callbacks.length; i++) {
                    var item = callbacks[i];
                    if (typeof item === "function") {
                        item(self.value);
                    }
                }
            });
        };

        try {
            executor(function resolve(result) {
                change('fulfilled', result);
            }, function reject(reason) {
                change('rejected', reason);
            });
        } catch (err) {
            change('rejected', err);
        }
    }

    /* function resolvePromise(promiseNew, x, resolve, reject) {
        if (x === promiseNew) throw new TypeError('Chaining cycle detected for promise #<Promise>');
        if (x instanceof Promise) {  //必须保证返回值是你自己构建的这个Promise类的实例
            // 返回结果是一个新的promise实例
            x.then(resolve, reject);
        } else {
            // 返回结果不是promise实例
            resolve(x);
        }
    } */
    function resolvePromise(promiseNew, x, resolve, reject) {
        if (x === promiseNew) throw new TypeError('Chaining cycle detected for promise #<Promise>');
        if ((x !== null && typeof x === "object") || (typeof x === "function")) {
            try {
                var then = x.then;
                if (typeof then === "function") {
                    // x是一个promise(类promise)实例:x.then(resolve,reject)
                    then.call(x, resolve, reject);
                } else {
                    resolve(x);
                }
            } catch (err) {
                reject(err);
            }
            return;
        }
        resolve(x);
    }
    Promise.prototype = {
        constructor: Promise,
        customize: true,
        then: function (onfulfilled, onrejected) {
            // onfulfilled/onrejected如果不是函数:未来保证穿透顺延的效果,我们需要为其设置默认的函数
            if (typeof onfulfilled !== "function") {
                onfulfilled = function onfulfilled(value) {
                    return value;
                };
            }
            if (typeof onrejected !== "function") {
                onrejected = function onrejected(value) {
                    throw value;
                };
            }

            var self = this,
                promiseNew = null;
            // 创建一个新的promise实例并且返回
            //   + 执行resolve/reject控制它成功或者失败
            //   + 到底是成功还是失败,是由onfulfilled/onrejected执行决定
            //       + onfulfilled/onrejected函数执行不报错,应该让其状态为成功,让其值为函数执行的返回值
            //       + 如果onfulfilled/onrejected返回的也是promise实例,那么promiseNew的状态和值和新返回的实例保持一致
            //       + 但是如果onfulfilled/onrejected返回的promise实例和promiseNew是一个东西,则直接抛出异常即可
            promiseNew = new Promise(function (resolve, reject) {
                switch (self.state) {
                    case 'fulfilled':
                        setTimeout(function () {
                            try {
                                var x = onfulfilled(self.value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    case 'rejected':
                        setTimeout(function () {
                            try {
                                var x = onrejected(self.value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    default:
                        // 把它存储到集合中,但是我们以后还要监听方法执行的结果,从而做其它事情
                        self.onFulfilledCallbacks.push(function (value) {
                            try {
                                var x = onfulfilled(value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        self.onRejectedCallbacks.push(function (value) {
                            try {
                                var x = onrejected(value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                }
            });
            return promiseNew;
        },
        catch: function (onrejected) {
            var self = this;
            return self.then(null, onrejected);
        }
    };

    // 暴露API
    if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = Promise;
    }
    if (typeof window !== 'undefined') {
        // window.Promise = Promise;
    }
})();

let p1 = new Promise((resolve, reject) => {
    // resolve(100);
    reject(0);
});
let p2 = p1.then(
    /* result => {
        return result;
    } */
    /* reason=>{
        throw reason;
    } */
);
p2.then(result => {
    console.log('成功', result);
}).then(null, reason => {
    console.log('失败', reason);
});
(function () {
    /* 工具类方法 */
    var isPromise = function isPromise(obj) {
        if ((obj !== null && typeof obj === "object") || (typeof obj === "function")) {
            if (typeof obj.then === "function") {
                return true;
            }
        }
        return false;
    };
    var resolvePromise = function resolvePromise(promiseNew, x, resolve, reject) {
        if (x === promiseNew) throw new TypeError('Chaining cycle detected for promise #<Promise>');
        if (isPromise(x)) {
            try {
                x.then(resolve, reject);
            } catch (err) {
                reject(err);
            }
            return;
        }
        resolve(x);
    };

    /* 构造函数 */
    function Promise(executor) {
        if (typeof executor !== 'function') throw new TypeError('Promise resolver ' + executor + ' is not a function');

        var self = this;
        self.state = 'pending';
        self.value = undefined;
        self.onFulfilledCallbacks = [];
        self.onRejectedCallbacks = [];
        var change = function change(state, value) {
            if (self.state !== 'pending') return;
            self.state = state;
            self.value = value;
            setTimeout(function () {
                var callbacks = self.state === 'fulfilled' ? self.onFulfilledCallbacks : self.onRejectedCallbacks;
                for (var i = 0; i < callbacks.length; i++) {
                    var item = callbacks[i];
                    if (typeof item === "function") {
                        item(self.value);
                    }
                }
            });
        };

        try {
            executor(function resolve(result) {
                change('fulfilled', result);
            }, function reject(reason) {
                change('rejected', reason);
            });
        } catch (err) {
            change('rejected', err);
        }
    }

    /* 原型 */
    Promise.prototype = {
        constructor: Promise,
        customize: true,
        then: function (onfulfilled, onrejected) {
            if (typeof onfulfilled !== "function") {
                onfulfilled = function onfulfilled(value) {
                    return value;
                };
            }
            if (typeof onrejected !== "function") {
                onrejected = function onrejected(value) {
                    throw value;
                };
            }
            var self = this,
                promiseNew = null;
            promiseNew = new Promise(function (resolve, reject) {
                switch (self.state) {
                    case 'fulfilled':
                        setTimeout(function () {
                            try {
                                var x = onfulfilled(self.value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    case 'rejected':
                        setTimeout(function () {
                            try {
                                var x = onrejected(self.value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    default:
                        self.onFulfilledCallbacks.push(function (value) {
                            try {
                                var x = onfulfilled(value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        self.onRejectedCallbacks.push(function (value) {
                            try {
                                var x = onrejected(value);
                                resolvePromise(promiseNew, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                }
            });
            return promiseNew;
        },
        catch: function (onrejected) {
            var self = this;
            return self.then(null, onrejected);
        }
    };  

    /* 对象 */
    Promise.resolve = function resolve(value) {
        return new Promise(function (resolve) {
            resolve(value);
        });
    };
    Promise.reject = function reject(value) {
        return new Promise(function (_, reject) {
            reject(value);
        });
    };
    Promise.all = function all(promises) {
        return new Promise(function (resolve, reject) {
            try {
                var index = 0,
                    len = promises.length,
                    results = [];
                for (var i = 0; i < len; i++) {
                    (function (i) {
                        var item = promises[i];
                        if (!isPromise(item)) {
                            index++;
                            results[i] = item;
                            index === len ? resolve(results) : null;
                            return;
                        }
                        item.then(function (result) {
                            index++;
                            results[i] = result;
                            index === len ? resolve(results) : null;
                        }, function (reason) {
                            reject(reason);
                        });
                    })(i);
                }
            } catch (err) {
                reject(err);
            }
        });
    };

    /* 暴露API */
    if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = Promise;
    }
    if (typeof window !== 'undefined') {
        window.Promise = Promise;
    }
})();


/* let p1 = Promise.resolve(10);
let p2 = new Promise(resolve => {
    setTimeout(function () {
        resolve(20);
    }, 000);
});
let p3 = new Promise((resolve, reject) => {
    setTimeout(function () {
        reject(30);
    }, 500);
});
let p4 = 40;

Promise.all([p1, p2, p3, p4]).then(results => {
    console.log('成功', results);
}).catch(reason => {
    console.log('失败', reason);
}); */

Iterator 遍历器 迭代器

/*
 * 遍历器(Iterator)是一种机制(接口):为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作,依次处理该数据结构的所有成员
 *   + 拥有next方法用于依次遍历数据结构的成员
 *   + 每一次遍历返回的结果是一个对象 {done:false,value:xxx}
 *     + done:记录是否遍历完成
 *     + value:当前遍历的结果
 * 
 * 拥有Symbol.iterator属性的数据结构(值),被称为可被遍历的,可以基于for of循环处理
 *   + 数组
 *   + 部分类数组:arguments/NodeList/HTMLCollection...
 *   + String
 *   + Set
 *   + Map
 *   + generator object
 *   + ...
 * 
 * 对象默认不具备Symbol.iterator,属于不可被遍历的数据结构
 */

let arr = [10, 20, 30, 40];
for (let item of arr) {
    // for of内部是按照 iterator.next 去迭代处理,所以只有具备 Symbol.iterator 属性「也就是具备Iterator迭代规范的」,才可以基于for of遍历 
    console.log(item);
} 


class Iterator {
    constructor(assemble) {
        // 挂载到实例的私有属性上:方便后期在方法中基于实例获取这些值
        // assemble:数字作为索引逐级递增、拥有length属性存储集合长度
        let self = this;
        self.assemble = assemble;
        self.index = 0;
    }
    next() {
        let self = this,
            assemble = self.assemble,
            index = self.index;
        if (index > assemble.length - 1) {
            // 遍历结束了
            return {
                done: true,
                value: undefined
            };
        }
        return {
            done: false,
            value: assemble[self.index++]
        };
    }
}
let itor = new Iterator([10, 20, 30, 40]);
console.log(itor.next()); //->{done:false,value:10}
console.log(itor.next()); //->{done:false,value:20}
console.log(itor.next()); //->{done:false,value:30}
console.log(itor.next()); //->{done:false,value:40}
console.log(itor.next()); //->{done:true,value:undefined}


let obj = {
    0: 10,
    1: 20,
    2: 30,
    3: 40,
    length: 4,
    // [Symbol.iterator]: Array.prototype[Symbol.iterator]
    /* [Symbol.iterator]: function () {
        return new Iterator(this);
    } */
    [Symbol.iterator]: function () {
        let self = this,
            index = 1;
        return {
            next() {
                if (index > self.length - 1) {
                    return {
                        done: true,
                        value: undefined
                    };
                }
                let result = {
                    done: false,
                    value: self[index]
                };
                index += 2;
                return result;
            }
        };
    }
};
for (let item of obj) {
    console.log(item);
} 

Generator 生成器

/*
 * 生成器对象是由一个generator function返回的,并且它符合可迭代协议和迭代器协议
 *   https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator
 * 
 * 普通函数 VS 生成器函数
 *    生成器函数 [[IsGenerator]]:true
 *    
 *   「把它当做一个实例 __proto__」
 *    普通函数是 Function 的实例,普通函数.__proto__===Function.prototype
 *    生成器函数是 GeneratorFunction 的实例,生成器函数.__proto__===GeneratorFunction.prototype -> GeneratorFunction.prototype.__proto__===Function.prototype
 *    ({}).toString.call(生成器函数) => "[object GeneratorFunction]"
 *    
 *   「把它作为一个构造函数 prototype」
 *    普通构造函数.prototype -> 原型对象(constructor:构造函数)
 *    生成器构造函数.prototype -> 原型对象(空对象:可以自己设置内容)
 *       生成器函数.prototype.__proto__ === Generator.prototype(next/return/throw/Symbol.toStringTag/Symbol.iterator...)
 */
 
 
function* func() {}
func.prototype.xxx = 'zhufeng';

// let itor = new func(); //Uncaught TypeError: func is not a constructor
let itor = func(); //=>创建func/Generator类的实例  并且是具备迭代器规范的(itor.next)
// itor.__proto__===func.prototype 
// itor.__proto__.__proto__===Generator.prototype  
console.log(itor); 
console.log(({}).toString.call(itor)); //=>"[object Generator]"


// Generator是基于Iterator迭代器规范管理Promise或者异步编程的;Promise是基于承诺模式管理异步编程;async/await是对Generator的进一步封装「语法糖」;
function* func() {
    console.log('A');
    yield 1;

    console.log('B');
    yield 2;

    console.log('C');
    yield 3;

    console.log('D');
    return 4;
}
let itor = func();
console.log(itor.next()); //->{done:false,value:1}
console.log(itor.next()); //->{done:false,value:2}
console.log(itor.next()); //->{done:false,value:3}
console.log(itor.next()); //->{done:true,value:4} */
/*
 * 创建了func的实例,但是和new执行不一样,func中的代码还没有执行
 * 当后续执行itor.next才会把这些代码执行
 * 并且每一次执行next遇到一个yield就结束
 *    + 每一次返回的结果是符合迭代器规范的
 *    + {done:true/false,value:yield后面的值或者是函数返回的值}
 */
 
 
// 执行next传递值,可以把传递值作为上一次yeild后的结果,但是yield后面跟的值是给每一次next执行后的value的
function* func() {
    let x = yield 1;
    console.log(x);
}
let itor = func();
console.log(itor.next()); //->{done:false,value:1}
console.log(itor.next(10)); //->{done:true,value:undefined}


function* func1() {
    yield 1;
    yield 2;
}
function* func2() {
    yield 3;
    yield* func1();
    yield 4;
}
let itor = func2();
console.log(itor.next()); //->{done:false,value:3}
console.log(itor.next()); //->{done:false,value:1}
console.log(itor.next()); //->{done:false,value:2}
console.log(itor.next()); //->{done:false,value:4}
console.log(itor.next()); //->{done:true,value:undefined}


function* func() {
    console.log(this); //=>window
    yield 1;
}
let itor = func();
itor.next();


// 本身基于promise管理的异步编程:每一次执行query,首先返回promise实例,1000ms后会让promise实例状态为成功,结果是传递进来的值+1
const query = x => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(++x);
        }, 1000);
    });
};

// AJAX(异步)串行的效果:第一个异步请求成功后再去发送第二个异步请求,第二个异步请求成功再去发送第三个请求...「一般上一个请求的结果对下一个请求很重要」

/* 基于promise及then链机制管理异步编程 */
query(1).then(result1 => {
    console.log(`第一个请求的结果:${result1}`);
    return query(result1);
}).then(result2 => {
    console.log(`第二个请求的结果:${result2}`);
    return query(result2);
}).then(result3 => {
    console.log(`第三个请求的结果:${result3}`);
});

/* 基于async/await实现 */
(async function () {
    let result1 = await query(1);
    console.log(`第一个请求的结果:${result1}`);

    let result2 = await query(result1);
    console.log(`第二个请求的结果:${result2}`);

    let result3 = await query(result2);
    console.log(`第三个请求的结果:${result3}`);
})();

/* 基于Generator管理 */
function* func(x) {
    let result1 = yield query(x);
    console.log(`第一个请求的结果:${result1}`);

    let result2 = yield query(result1);
    console.log(`第二个请求的结果:${result2}`);

    let result3 = yield query(result2);
    console.log(`第三个请求的结果:${result3}`);
}
let itor = func(1);
itor.next().value.then(result1 => {
    itor.next(result1).value.then(result2 => {
        itor.next(result2).value.then(result3 => {
            itor.next(result3);
        });
    });
});


var isPromise = function isPromise(obj) {
    if ((obj !== null && typeof obj === "object") || (typeof obj === "function")) {
        if (typeof obj.then === "function") {
            return true;
        }
    }
    return false;
};

function AsyncFunc(generator, ...params) {
    const itor = generator(...params);
    const next = x => {
        let {
            value,
            done
        } = itor.next(x);
        if (done) return;
        // value.then(result => next(result));
        !isPromise(value) ? value = Promise.resolve(value) : null;
        value.then(next);
    };
    next();
}
AsyncFunc(function* (x) {
    let result1 = yield query(x);
    console.log(`第一个请求的结果:${result1}`);

    let result2 = yield query(result1);
    console.log(`第二个请求的结果:${result2}`);

    let result3 = yield query(result2);
    console.log(`第三个请求的结果:${result3}`);

    yield 10;
    console.log('OK');
}, 1);

浏览器底层渲染机制

/*
 * 客户端从服务器获取到需要渲染页面的源代码后
 * 开辟一个GUI渲染线程,自上而下解析代码,最后绘制出对应的页面
 *
 * 自上而下渲染解析的过程是同步的,但有些操作也是异步的
 * 
 * 1. 关于css资源的加载
 *    - 遇到的是<style> "内嵌样式"   
 *        "同步" 交给GUI渲染线程解析
 *    - 遇到的是<link> "外链样式"
 *        "异步" 开辟一个新的"HTTP网络请求线程"  
 *            (同一个源下,根据不同的浏览器,最多只允许同时开辟4-7个HTTP线程 "HTTP的并发数")
 *        不等待资源信息加载回来,GUI渲染线程继续向下渲染
 *        GUI渲染线程同步操作都处理完成后,再把基于HTTP网络请求线程请求回来的资源文件进行解析渲染
 *    - 遇到@import "导入样式"
 *    - "同步" 开辟一个新的"HTTP网络请求线程"去请求资源文件
 *    - 但是在资源文件没有请求回来之前,GUI渲染线程会被"阻塞",不允许其继续向下渲染
 * 
 * 2. 遇到srcipt资源的加载
 *    - 默认都是"同步"的:必须基于HTTP网络线程,把资源请求回来之后,并且交给"JS渲染线程"渲染解析完成后,GUI渲染线程才能继续向下渲染,所以<script>默认也是"阻塞GUI渲染"的
 *    - async <script async>开辟新的HTTP网络请求线程,同时GUI渲染线程继续向下渲染解析,但是一旦资源请求回来后,会中断GUI的渲染,先把请求回来的JS进行渲染解析
 *    - defer <script defer>和async类似,开辟新的HTTP网络请求线程,同时GUI渲染线程继续向下渲染解析,但是不一样的是,defer和link类似,是在GUI同步代码渲染完成之后,才会渲染解析JS
 *
 * 3. 遇到img或者音视频资源的加载
 *    - 遇到这些,也会发送新的HTTP网络线程,请求加载资源文件,不会阻碍GUI的渲染,当GUI渲染完成后,才会把请求回来的资源信息进行解析
 *
 * Webkit浏览器预测解析:chrome的预加载扫描器html-preload-scanner通过扫描节点中的 “src” , “link”等属性,找到外部连接资源后进行预加载,避免了资源加载的等待时间,同样实现了提前加载以及加载和执行分离
 */

image.png

/* 
 * 页面渲染的步骤:
 *     - DOM TREE(DOM树):自上而下渲染完页面,整理好整个页面的DOM结构关系
 *     - CSSOM TREE(样式树):当把所有的样式资源请求加载回来后,按照引入css的顺序,依次渲染样式代码,生成样式树
 *     - RENDER TREE(渲染树):把生成的DOM树和CSSOM树结合在一起,生成渲染树(设置display:none的元素不进行处理)
 *     - Layout 布局/回流/重排:根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小
 *     - 分层处理:按照层级定位分层处理,每一个层级都会详细规划出具体的绘制步骤
 *     - Painting 重绘:按照每一层级计算处理的绘制步骤,开始绘制页面
 *
 *
 *
 * 前端性能优化 (CRP: 关键渲染路径)
 *     - 生成DOM TREE
 *         - 减少DOM层级嵌套
 *         - 不要使用"非标准"标签
 *         - ...
 *     - 生成CSSOM TREE
 *         - 尽可能不要使用@import(阻塞GUI渲染)
 *         - 如果CSS代码较少,尽可能使用"style内嵌样式"(减少HTTP网络请求)(尤其是移动端开发)
 *         - 如果使用link,尽可能把所有样式资源合并为一个,且压缩(减少HTTP网络请求数量,以及渲染CSS的时候,也不需要再计算依赖关系...)
 *         - CSS选择器链短一些(因为CSS选择器渲染是从右到左)
 *         - 把link等导入css的操作放在head当中(目的是:一加载页面就开始请求资源,同时GUI去生成DOM树 "css等资源预先加载")
 *         - ...
 *     - 对于其他资源的优化
 *         - 对于<script>,尽可能放置在页面的底部(防止其阻碍GUI渲染);对于部分<script>需要使用async或者defer
 *           - async是不管JS的依赖关系的,谁先获取到就先执行
 *           - defer不会这样,和link一样,是等待所有的<script defer>都请求回来后,按照导入顺序/依赖关系依次渲染执行
 *         - 对于<img>
 *           - 懒加载:第一次加载页面不要加载请求图片,哪怕它是异步的,但是也占据了HTTP并发数量,导致其他资源延后加载
 *           - base64:不用请求图片,base64码基本上代表的就是图片,而且页面渲染图片的时候速度也会很快(慎用,但是在webpack工程化中可以使用,它基于file-loader可以自动base64)(速度快是因为省去了加载、解码编码的过程)
 *     - 生成RENDER TREE
 *     - Layout/Painting:重要的优化手段(减少DOM回流/重排和重绘)
 *         - 第一次加载页面必然会有一次回流和重绘
 *         - 触发回流操作后,也必然会触发重绘;如果只是单纯重绘,则不会引发回流;性能优化点,重点在回流上
 */
 
 // 操作DOM消耗性能? =>DOM回流
    - 元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局导致渲染树重新计算布局和渲染
        - 元素在视口中的大小或者位置的发生变化
        - 元素的删除或者新增(以及基于display控制显示隐藏)
        - 内容发生变化(比如文本变化或者图片被另一个不同尺寸的图片代替)
        - 回流是根据视口的大小来计算元素的位置和大小的,所以浏览器窗口尺寸变化也会引起回流
    这些操作都需要浏览器重新计算每一个元素在视口中的位置和大小(也就是重新Layout/Reflow)
    - 避免DOM回流方式
  
// 当代浏览器的渲染队列机制:在当前上下文中,遇到一行修改样式的代码,并没有立即通知浏览器渲染,而是把其放置在渲染队列中,接下来看是否还有修改样式的代码,如果有继续放置在渲染队列中...一直到再也没有修改样式的代码或者"遇到一行获取样式的操作",这样都会刷新浏览器的渲染队列机制(也就是把现在队列中修改样式的操作,统一告诉浏览器渲染,这样只会引发一次回流)
box.style.width = '200px'
box.style.height = '400px'
box.offsetHeight //box.style.xxx  window.getComputedStyle(box).xxx  box.currentStyle.xxx  box.clientWidth|Height|Left|Top  box.offsetWidth|Height|Left|Top  box.scrollWidth|Height|Left|Top... 这些获取样式的操作都会刷新浏览器渲染队列
box.style.position = 'absolute'
box.style.top = '100px'

// 1.放弃传统操作DOM的时代,基于vue/react
// 2.样式的"分离读写":把修改样式和获取样式分离开
box.style.width = '200px'
box.style.height = '400px'
box.style.position = 'absolute'
box.style.top = '100px'
box.offsetHeight

//统一修改样式也是为了分离读写,修改的时候不要有获取操作
box.style.csstext = 'width:100px;height:200px;...'
box.className = 'box'

// 缓存布局信息
box.style.width = box.offsetWidth + 10 + 'px'
box.style.height = box.offsetHeight + 10 + 'px'

let w = box.offsetWidth,
    h = box.offsetHeight
box.style.width = w + 10 + 'px'
box.style.height = h + 10 + 'px'


// 3. 元素批量修改
let arr = [1,2,3]
arr.forEach(item => {
    let span = document.createElement('span')
    span.innerText = item
    document.body.appendChild(span)
})

// 模板字符串:可能因为把原始容器的内容变为字符串和新的字符串拼接,最后再整体渲染回去,导致原始容器中的元素绑定的一些事件失效
let str = ``
arr.forEach(item => {
    str += `<span>${item}</span>`
})
// document.body.innerHTML += str
document.body.innerHTML = str

// 文档碎片createDocumentFragment:临时存放元素对象的容器
let frag = document.createDocumentFragment()
arr.forEach(item => {
    let span = document.createElement('span')
    span.innerText = item
    frag.appendChild(span)
})
document.body.appendChild(frag)
frag = null


// 4. 动画效果应用到position属性为fixed或者absolute的元素上(脱离文档流)
// 5. CSS3硬件加速(GPU加速):修改transform/opacity/filters...(浏览器硬件加速,弊端就是消耗内存)

setTimeout(() => {
    // 立即回到left:0的位置
    box.style.transitionDuration = '0s'
    box.style.left = 0
    // 刷新渲染队列
    box.offsetWidth
    // 回到开始位置后,再次运动到left:200位置(有动画)
    box.style.transitionDuration = '0.5s'
    box.style.left = '200px'
}, 1000)

image.png

image.png

image.png