promise无敌篇

78 阅读5分钟

高阶函数:一个函数的参数或返回值为函数时,这个函数就是高阶函数

  • 作用:对原因函数进行扩展
function isType(typing) {
  return (val) => {
    return (Object.prototype.toString.call(val)) === `[object ${typing}]`
  }
}
let utils = {};
['Number', 'Boolean', 'String'].forEach(type => {
  utils[`is${type}`] = isType(type)
});
console.log(utils.isNumber('abc'))

函数柯理化:将一个函数的功能拆分成多个函数

  • 作用:可以让函数变小,基于高阶函数,核心就是缓存变量
Promise
  1. 通过new Promise创建一个实例,Promise构造函数中接收一个回调参数excutor
  2. excutor函数接收两个参数resolve,reject
  3. 调用resolve会让promise变成成功状态,reject被调用会变成失败状态,状态一旦被确定成功/失败则不能改变
    • 默认是pending 等待状态
    • fufilled 成功状态
    • rejected 失败状态
  4. 每个promise实例都有一个then方法,有两个参数
    • onfufilled 成功回调
    • onrejected 失败回调
  5. 如果不调用resolve/reject,则一直处于pending状态
  6. excutor是立即执行的
  7. promise是链式调用
    • 返回非Promise的值/undefined,则返回成功状态的promise
    • 返回reject/报错,则返回失败状态的promise
promise的实现
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 = [];
    // promise调用then的时候 可能状态依旧是pending,那么我们需要将回调先存放起来
    // 等待过一会调用resolve时触发 onResolvedCallbacks 执行
    // 等待调用 reject时触发onRejectedCallbacks 执行

    const resolve = (value) => {
      if (value instanceof Promise) {
        // 递归解析的流程
        return value.then(resolve, reject);
      }
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach((cb) => cb());
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((cb) => cb());
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  catch(errCallback) {
    return this.then(null, errCallback);
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (e) => {
          throw e;
        };
    let promise2 = new Promise((resolve, reject) => {
      // 调用then的时候 已经确定了是成功还是失败了
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);

            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
      }
    });
    return promise2;
  }
}
resolvePromise的实现
  • 处理promise的返回值
function resolvePromise(promise2, x, resolve, reject) {
  if (x === promise2)
    return reject(new TypeError("Chaining cycle detected for promise"));

  // 我如何知道x是不是promise
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // 有可能是promise
    let called = false;
    try {
      let then = x.then; // then方法可能是通过defineProperty来进行定义的
      if (typeof then === "function") {
        // 是promise  {then:function(){}}
        then.call(x, (y) => {
          // 为了防止promise解析后的结果依旧是promise,所以需要递归解析
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, (r) => {
          if (called) return;
          called = true;
          reject(r);
        }
        );
      } else {
        // 就是一个对象或者函数  {a:1}  function(){}
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x); // 普通值 直接将结果传递到下面就可以了
  }
}
resolve和reject的实现
Promise.resolve(value) {
    return new Promise((resolve, reject) => {
      resolve(value);
    });
}

Promise.reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
}
all的实现
  • 全部成功则返回
Promise.all = (callbacks) => {
  return new Promise((resolve, reject) => {
    let result = []
    let index = 0
    callbacks.forEach((item, index) => {
      Promise.resolve(item).then(value => {
        result[index] = value // 存储结果
        if (++index === callbacks.length) {
          resolve(result)
        }
      // 失败直接reject
      }, reject)
    })
  })
}
finally的实现
  • 无论成功和失败都要执行的逻辑
// 实现
Promise.prototype.finally = (fn) => {
  return this.then((val) => {
    return Promise.resolve(fn()).then(() => val)
  }, (err) => {
    return Promise.resolve(fn()).then(() => { throw err })
  })
}
// 使用
Promise.resolve('abc').finally(() => { // 无论成功还是失败都会执行
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('abcasdasda')
    }, 1000)
  })
}).then((value) => {
  console.log('成功', value)
}).catch((value) => {
  console.log('失败', value)
})
allSettled
  • 不管失败/成功的请求全部返回
race的实现
  • 返回第一个成功的promise
Promise.race = (values) => {
  return new Promise((resolve, reject) => {
    this.values.forEach((item) => {
      Promise.resolve(item).then(resolve, reject)
    })
  })
}
generator(生成器)
  • async + await 前身 generator
  • generator 是一个语法 , 可以让我们的异步代码 写的更像同步
const fs = require("fs/promises");
const path = require('path')
function* readFile() {
  let data1 = yield fs.readFile(path.resolve(__dirname, "a.txt"), "utf8");
  let data2 = yield fs.readFile(path.resolve(__dirname, data1), "utf8");
  return data2; // 30
}

function co(iter) {
  return new Promise((resolve, reject) => {
    const step = (data) => {
      const { value, done } = iter.next(data)
      if (done) { // 完成返回值
        resolve(value)
        return
      }
      Promise.resolve(value).then(res => {
        step(data)
      }).catch(err => {
        reject(err)
      })
    }
    step()
  })
}

co(readFile()).then(data => {
  console.log(data);
}).catch(e => {
  console.log(e);
})
async + await
  • ( generator + co) 语法糖
  • async函数返回的就是一个promise co(readFile())
const fs = require("fs/promises");
const path = require('path')
async function readFile() {
  try {
    let data1 = await fs.readFile(path.resolve(__dirname, "a.txt"), "utf8");
    let data2 = await fs.readFile(path.resolve(__dirname, data1), "utf8");
    return data2; // 30
  } catch (e) {
    console.log(e)
  }
}
readFile().then(data=>{
  console.log(data)
})
扩展:进程和线程
  1. 进程是系统进行分配资源的最小单位,计算机是以进程来分配任务和调度任务
  2. 每个进程都是独立的,但是可以通信(通过ipc)
  3. 进程中包含着线程
  • 浏览器是由多个进程组成的,浏览器中主要的进程有:
    • 主进程
    • 网络进程
    • 绘图的进程
    • 插件都是一个个的进程
  • 每个页签都是一个独立的进程 (渲染进程): 渲染的线程
  • 渲染进程中 : 进程中包含的线程
    • ui线程 负责页面渲染、布局、绘制
    • js线程:js引擎线程, 主要负责执行js代码的 (ui线程 js线程互斥的,js也是单线程的) 为了保证渲的一致性要保证整个执行是单线程的
    • 主线程:单线程的 (代码是从上大下执行) webworker(不能操作dom元素) 同步代码
    • 定时器、请求、用户的事件操作 都是异步的 (每次开一个定时器 都会生成一个新的线程)
异步任务的划分 : 宏任务、
  • 微任务:
    • Promise.then(ECMAScript里面提供的)
    • mutationObserver(html5,这里的回调是一异步执行的)
    • queueMircrotask
  • 宏任务:
    • 默认的script脚本 ui渲染也是一个宏任务
    • setTimout
    • 网络请求
    • 用户的事件、
    • messageChannel
    • setImmediate
  • requestIDleCallback requestFrameAnimation (这个是放在渲染的) 只能算回调,你可以当成宏任务
  • 代码执行的过程中
    • 宏任务和微任务 会将对应的结果放到不同的队列中
    • 等待当前宏任务执行完毕后,会将微任务全部清空 (在微任务执行的过程中 如果在产生微任务 则会将当前产生的微任务放到队列尾部 )
    • 微任务都执行完毕后,会在宏任务队列中拿出来一个执行 (宏任务每次执行一个,微任务每次执行一堆)
  • js 是单线程的所以要有一个事件触发线程 来实现任务的调度
  • 宏任务的执行顺序 是按照 调用的顺序 (时间一样的情况下), 如果时间不一样,则以放入的顺序为准
  • 渲染是要在特定的时机才能渲染, 根据浏览器的刷新频率 16.6ms, 不是每一轮都要渲染的 (一定在微任务之后)