Loading...
墨滴

斌bin

2021/03/06  阅读:15  主题:默认主题

js系列之sort 函数原理解析

js 底层算法的手写实现

sort
sort
  • 缘起:

js 异步编程一直广泛应用在业务中,常见的异步编程示例是: 回调函数,事件监听,Promise , Generator , promise , async/await 等; 所以有必要自己整理一下这一块的知识, 作为面试中的重灾区,有必要在这里进行总结;

  • js 异步编程:

最开始的时候,通过回调函数的方式来实现异步编程,像setTimeout , setInterval ,但是业务复杂的情况下,会陷入异步回调地狱的问题,为了解决这个问题,promise出现了, 举例说:

function returnPromise (arr) {
    return new promise(resolve , reject => {
        if(arr.length > 0){
          resolve();
        }else {
          reject();
        }
    });
}

const myanswer = returnPromise(arr);
myanswer.then(res => {

}).then((res) => {

}).catch(err => {

}); 

上述是使用promise 的代码方式,但是呢, 遇到多个promise的情况下 , 这一种写的方式也是不优雅的; Generator 方式应运而生, Generator 是异步任务的一种容器, Generator 配合 yield 进行实现;示例代码如下:

functiontestGenerator(){
  let a = yield 111;
  console.log(a);
  let b = yield 222;
  console.log(b);
  let c = yield 333;
  console.log(c);
}

let t = testGenerator();
t.next(1); // 输出1;
t.next(2); // 输出2;
t.next(3); // 输出3;

上述是Generator 函数的使用方式; 之后又发展出了async / await 的方式 ; async / await 的使用方式使得异步编程的代码可以写成同步的方式:

代码示例:

function returnPromise (arr) {
    return new promise(resolve , reject => {
        if(arr.length > 0){
          resolve();
        }else {
          reject();
        }
    });
}

async usingreturnPromise = (arr) =>{
  let result = await returnPromise(arr);
  if(result) {
    // 执行true 的逻辑;
  }else {
    // 执行false的逻辑;
  }
}

上面是使用async \ await 方式实现的形式,这里需要说明的一点是await 可以返回一个promise 对象,也可以返回一个值对象,所以,async 里面使用await 修饰一个普通的函数也是可以的;

  • pomise 的原生实现:

手动实现一个promise , 首先要知道他的状态 , promise 的三种状态是fullfilled , pending , rejected ; resolve 和 reject 同时返回一个promise 对象,promise 通过prototype 的方式可以继续调用,在写promise的原生实现之前 ,首先是要先熟悉为了解决异步回调地狱的问题 , promise 的设计思路:

promise 使用回调函数延迟绑定、 返回值穿透、错误冒泡的方式进行实现; 除了这三个基本的设计思路,promise 还有四个基本的api 可供调用:

  1. all
  2. allSettled
  3. race
  4. any

首先说明all 方法: 使用promise.all的时候,resolve 的情况下,promise 会按照顺序返回promise 的执行情况,如果遇到rejected 的情况,promise.all会返回第一个出现reject 的情况; allSettled 不会返回失败的情况,只会返回每个promise 执行的情况;

const resolved = Promise.resolve(1);
const rejected = Promise.reject(-1);
Promise.all([resolved , rejected]).then (res => {
  console.log(res);
});

/**
__proto__: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: -1
*/

上面是allSettled 的执行结果; Promise.race 首先是返回第一个状态生成的Promise对象,图片加载这一个业务可以使用Promise.race 进行实现, race首先是返回promise 迭代对象中有状态返回的那个promise (说的有点low , 懂得都懂);

const getImage = () => {
  return new Promise((resolve,reject) => {
      fetch(url, {
          name:'testPromise'
      }).then((res) => {
        resolve(res);
      });
  });
}
const timeout = () => {
  return new Promise(resolve , reject => {
      setTimeout(() => console.log('timeout '),1000);
  });
}

const raceResult = Promise.race([getImage , timeout]).then(res => {
  console.log(res);
}).catch(err => {
  console.log(err);
})

如果遇到timeout 首先执行的情况,说明获取图片是失败的; 使用race 是可以实现这一个业务需求的;

Promise.any 方式是参数只要有一个成功 , 就返回那个成功的结果,所有都失败的话 , promise 实例变成rejected 的状态;

上述是在手动实现Promise 之前需要的基础知识;

  • Generator:

接着介绍一下Generator 的基本使用,Generator是一个函数的容器; 上代码:

functiongen1() {

    yield 1;

    yield* gen2();

    yield 4;

}

functiongen2() {

    yield 2;

    yield 3;

}

var g = gen1();

console.log(g.next())

console.log(g.next())

console.log(g.next())

console.log(g.next())

// output:

// { value: 1, donefalse }

// { value: 2, donefalse }

// { value: 3, donefalse }

// { value: 4, donefalse }

// {value: undefined, donetrue}

yield 经常配合Generator 实现控制函数的执行、暂停功能;

其实, Generator 的功能远远不止着一些 , 使用Generator 可以和thunk 结合 , 使用thunk 功能减少代码的复用:

thunk 集合Generator 的实例是:

// thunk 写法; 类似于函数柯里化的概念;
  const readFileThunk = (filename) => {
      return (callback) => {
          fn.readFile(filename,callback);
      }
  }
  
  function run(gen){
      const next = (err , data) => {
        let res = gen.next();
        if(res.done) return;
        res.value(next);
      }
      next();//继续执行该函数;
  }

除了Generator 和 thunk 结合之外 , Generator 还可以和promise 结合使用;

上代码:

// 最后包装成 Promise 对象进行返回

const readFilePromise = (filename) => {

  return new Promise((resolve, reject) => {

    fs.readFile(filename, (err, data) => {
      if(err) {
        reject(err);
      }else {
        resolve(data);
      }
    });
  }).then(res => res);
}

// 这块和上面 thunk 的方式一样
const gen = function* () {
  const data1 = yield readFilePromise('1.txt')
  console.log(data1.toString())
  const data2 = yield readFilePromise('2.txt')
  console.log(data2.toString)
}

// 这块和上面 thunk 的方式一样

function run(gen){
  const next = (err, data) => {
    let res = gen.next(data);
    if(res.done) return;
    res.value.then(next);
  }

  next();
}

run(g);

这是Promise 和 thunk 的结合方式; run 函数的写法就是thunk 的实现形式;

另外 , 关于一点还需要说一下 , co函数库结合Generator 也是异步的解决方案之一; co函数库的源码也是值得学习的,学习一下co函数库作者TJ的编程方式;

about me: web 前端开发一年半 , 金三银四最近在看机会 , 欢迎交流; wechat number : Yingbin192;

斌bin

2021/03/06  阅读:15  主题:默认主题

作者介绍

斌bin