Skip to content

js

overview

core-content

event-loop

browser-runtime

requestAnimationFrame
requestIdleCallback
EventLoop 和浏览器渲染、帧动画、空闲回调的关系

node-runtime

  • 参考:
    https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick
    https://zhuanlan.zhihu.com/p/35918797
  • Phases Overview:
    timers: this phase executes callbacks scheduled by setTimeout() and setInterval().
    pending callbacks: executes I/O callbacks deferred to the next loop iteration.
    idle, prepare: only used internally.
    poll: retrieve new I/O events; execute I/O related callbacks (almost all with the exception of close callbacks, the ones scheduled by timers, and setImmediate()); node will block here when appropriate.
    check: setImmediate() callbacks are invoked here.
    close callbacks: some close callbacks, e.g. socket.on('close', ...).
  • 参考机制图解:

输入输出判断

  • q1:
js
setTimeout(() => {
  console.log('setTimeout start');
  new Promise((resolve) => {
    console.log('promise1 start');
    resolve();
  }).then(() => {
    console.log('promise1 end');
  })
  console.log('setTimeout end');
}, 0);
function promise2() {
  return new Promise((resolve) => {
    console.log('promise2');
    resolve();
  })
}
async function async1() {
  console.log('async1 start');
  await promise2();
  console.log('async1 end');
}
async1();
console.log('script end');
  • q2:
js
const fs = require('fs');
fs.readFile(__filename, (data) => {
    // poll(I/O 回调) 阶段
    console.log('readFile')
    Promise.resolve().then(() => {
        console.error('promise1')
    })
    Promise.resolve().then(() => {
        console.error('promise2')
    })
});
setTimeout(() => {
    // timers 阶段
    console.log('timeout');
    Promise.resolve().then(() => {
        console.error('promise3')
    })
    Promise.resolve().then(() => {
        console.error('promise4')
    })
}, 0);
// 下面代码只是为了同步阻塞1秒钟,确保上面的异步任务已经准备好了
var startTime = new Date().getTime();
var endTime = startTime;
while(endTime - startTime < 1000) {
    endTime = new Date().getTime();
}
// 最终输出 timeout promise3 promise4 readFile promise1 promise2
  • await一个同步函数
js

console.log('start');
setTimeout(() => {
    console.log('timeout');
}, 0);
new Promise((resolve, reject) => {
    console.log('promise测试');
    resolve()
    console.log('promise测试 after for-loop');
}).then(() => {
    console.log('promise测试1');
}).then(() => {
    console.log('promise测试2');
})
function fn1() {
    console.log('await 1')
}
async function fn() {
    console.log('fn start')
    const res = await fn1()
    console.log('fn end')
}
fn()
console.log('end');
  • 微任务事件: 执行时产生了微任务,会继续丢到微任务队列中去;
js

const genMicroTask = (value)=> {
    new Promise((resolve)=>{
    resolve(1)
  }).then(()=>{
    console.log("test1",value)
  })
}

new Promise((resolve)=> {
  resolve(1)
}).then((value)=>{
  console.log(2)
  genMicroTask(3)
  setTimeout(()=> {
    console.log(4)
  },0)
  genMicroTask(5)
})

浏览器性能分析


js-bridge

首先来说Native端调用Web端,这个比较简单,JavaScript作为解释性语言,最大的一个特性就是可以随时随地地通过解释器执行一段JS代码,所以可以将拼接的JavaScript代码字符串,传入JS解析器执行就可以,JS解析器在这里就是webView;

  • Web->Native:

拦截Webview请求的URL Schema;向Webview中注入JS API;


同源和跨域


微前端


低代码


js基础

闭包

js
const fn = () => {
    let increment = 0;
    const add = () => {
        increment++;
        console.log(increment);
    }
    console.log('fn() called', increment);
    return [increment, add]
}

const [increment, add] = fn();
console.log(increment);
add();
add();
add();
console.log(increment);

const fn1 = () => {
    let obj = {
        increment: 0
    };
    const add1 = () => {
        obj.increment++;
        console.log(obj);
    }
    console.log('fn() called', obj);
    return [obj, add1]
}

const [obj, add1] = fn1();
console.log(obj);
add1();
console.log(increment);

promise

sh

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    // 存放成功的回调
    this.onResolvedCallbacks = [];
    // 存放失败的回调
    this.onRejectedCallbacks= [];

    let resolve = (value) => {
      if(this.status ===  PENDING) {
        this.status = FULFILLED;
        this.value = value;
        // 依次将对应的函数执行
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    } 

    let reject = (reason) => {
      if(this.status ===  PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        // 依次将对应的函数执行
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    }

    try {
      executor(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    }

    if (this.status === REJECTED) {
      onRejected(this.reason)
    }

    if (this.status === PENDING) {
      // 如果promise的状态是 pending,需要将 onFulfilled onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value)
      });

      // 如果promise的状态是 pending,需要将 onFulfilled onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
      this.onRejectedCallbacks.push(()=> {
        onRejected(this.reason);
      })
    }
  }
}
  • version2:
sh
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

const resolvePromise = (promise2, x, resolve, reject) => {
  // 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise  Promise/A+ 2.3.1
  if (promise2 === x) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }
  // Promise/A+ 2.3.3.3.3 只能调用一次
  let called;
  // 后续的条件要严格判断 保证代码能和别的库一起使用
  if ((typeof x === "object" && x != null) || typeof x === "function") {
    try {
      // 为了判断 resolve 过的就不用再 reject 了(比如 reject resolve 同时调用的时候)  Promise/A+ 2.3.3.1
      let then = x.then;
      if (typeof then === "function") {
        // 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty  Promise/A+ 2.3.3.3
        then.call(
          x,
          (y) => {
            // 根据 promise 的状态决定是成功还是失败
            if (called) return;
            called = true;
            // 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            // 只要失败就失败 Promise/A+ 2.3.3.3.2
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4
        resolve(x);
      }
    } catch (e) {
      // Promise/A+ 2.3.3.2
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4
    resolve(x);
  }
};

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    let resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };

    let reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    //解决 onFufilled,onRejected 没有传值的问题
    //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
    //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后 then resolve 中捕获
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };
    // 每次调用 then 都返回一个新的 promise  Promise/A+ 2.2.7
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) {
        //Promise/A+ 2.2.2
        //Promise/A+ 2.2.4 --- setTimeout
        setTimeout(() => {
          try {
            //Promise/A+ 2.2.7.1
            let x = onFulfilled(this.value);
            // x可能是一个proimise
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            //Promise/A+ 2.2.7.2
            reject(e);
          }
        }, 0);
      }

      if (this.status === REJECTED) {
        //Promise/A+ 2.2.3
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
}

Promise.prototype.catch = function (errCallback) {
  return this.then(null, errCallback);
};

Promise.prototype.finally = function (callback) {
  return this.then(
    (value) => {
      return Promise.resolve(callback()).then(() => value);
    },
    (reason) => {
      return Promise.resolve(callback()).then(() => {
        throw reason;
      });
    }
  );
};

Promise.all = function (values) {
  if (!Array.isArray(values)) {
    const type = typeof values;
    return new TypeError(`TypeError: ${type} ${values} is not iterable`);
  }

  return new Promise((resolve, reject) => {
    let resultArr = [];
    let orderIndex = 0;
    const processResultByKey = (value, index) => {
      resultArr[index] = value;
      if (++orderIndex === values.length) {
        resolve(resultArr);
      }
    };
    for (let i = 0; i < values.length; i++) {
      let value = values[i];
      if (value && typeof value.then === "function") {
        value.then((value) => {
          processResultByKey(value, i);
        }, reject);
      } else {
        processResultByKey(value, i);
      }
    }
  });
};

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    // 一起执行就是for循环
    for (let i = 0; i < promises.length; i++) {
      let val = promises[i];
      if (val && typeof val.then === "function") {
        val.then(resolve, reject);
      } else {
        // 普通值
        resolve(val);
      }
    }
  });
};

const promisify = (fn) => {
  // 典型的高阶函数 参数是函数 返回值是函数
  return (...args) => {
    return new Promise((resolve, reject) => {
      fn(...args, function (err, data) {
        // node中的回调函数的参数 第一个永远是error
        if (err) return reject(err);
        resolve(data);
      });
    });
  };
};
promise链式调用
  • 题目:
js
fetched(0, () => {
console.log(1);
});
fetched(500, () => {
console.log(2);
});
fetched(100, () => {
console.log(3);
});
fetched(200, () => {
console.log(4);
});
// output: 1, 2, 3, 4
js

let originPromise = Promise.resolve();
const fetched = (time, cb) => {
    originPromise = originPromise.then(() => {
        return new Promise((resolve) => {
            setTimeout(() => {
                cb();
                resolve();
            }, time);
        });
    });
}

fetched(0, () => {
    console.log(1);
    });
fetched(500, () => {
console.log(2);
});
fetched(100, () => {
console.log(3);
});
fetched(200, () => {
console.log(4);
});
js并发限制多个请求
js
// 并发请求函数
const concurrencyRequest = (urls, maxNum) => {
    return new Promise((resolve) => {
        if (urls.length === 0) {
            resolve([]);
            return;
        }
        const results = [];
        let index = 0; // 下一个请求的下标
        let count = 0; // 当前请求完成的数量

        // 发送请求
        async function request() {
            if (index === urls.length) return;
            const i = index; // 保存序号,使result和urls相对应
            const url = urls[index];
            index++;
            console.log(url);
            try {
                const resp = await fetch(url);
                // resp 加入到results
                results[i] = resp;
            } catch (err) {
                // err 加入到results
                results[i] = err;
            } finally {
                count++;
                // 判断是否所有的请求都已完成
                if (count === urls.length) {
                    console.log('完成了');
                    resolve(results);
                }
                request();
            }
        }

        // maxNum和urls.length取最小进行调用
        const times = Math.min(maxNum, urls.length);
        for(let i = 0; i < times; i++) {
            request();
        }
    })
}
js最大同时请求限制
js
const realFetch = (...arg)=> {
   return fetch(arg)
}

const queue = []

const maxCurrentFetchLimit = 5
let currentFetchNum = 0

const checkCurrentFetchNum = () => currentFetchNum < maxCurrentFetchLimit;
const editCurrentFetchNum = (arg) => {
    currentFetchNum = currentFetchNum + arg
}

const fetch = (...args) => {
    return new Promise((resolve,reject)=> {
        const newRequest = {
            arg: args,
            // id: String(Date.now()) + Math.random(100),
            resolveCb: resolve,
            rejectCb: reject
        }
        queue.push(newRequest);
    })
}

setInterval(()=> {
    if(queue.length > 0 && checkCurrentFetchNum()) {
        const req1 = queue.shift();
        editCurrentFetchNum(1)
        realFetch(req1.arg).then(res => {
           editCurrentFetchNum(-1)
           req1.resolveCb(res) 
        })
    }
}, 0)

js,blob, stream

js变量提升

js

if (!("a" in window)) {
    var a = 1;
}
console.log(a); // undefined