彻底读懂promise------手写实现它

388 阅读3分钟

前言

promise是前端同学最熟悉不过的概念了,几乎每一位都在面试场中经历过它,日常的开发工作中也在使用且仅仅在使用,很多人没有深究原理,因此我带大家彻底搞懂promise原理,实现自己的小突破。如果你想自己写一个库或者公司需要实现自己的promise就可以用到本篇知识。

写在前面

介绍

我们使用反向推理,因此要想实现promise,首先我们要熟练使用,在根据我们使用的功能,依次实现它。首先需要阅读下promise A+ 规范 promisesaplus.com/

简单介绍一下promise的功能:

  • promise是es6的新特性,是一种异步解决方案,但同时会存在异步嵌套问题;
  • 每个promise有三个状态: pending resolve reject; resolve标识变成成功态:fulfilled,reject标识变成失败态:rejected;抛出异常也会走向失败态(throw new Error("error message"));
  • 每个promise都有then方法和catch方法;then方法传入两个参数:一个是成功的回调,一个是失败的回调

promise解决了什么问题呢?

回调地狱问题(回调函数解决的); 异步并发问题

如何解决异步并发问题?

  • 回调函数
const fs = require('fs'); // 可以读取文件

interface IPerson {
    age:number,
    name:string,
    [xxx: string]: any
}
function after(times: number,callback: Function){ // 高阶函数  可以暂存变量
    let obj = {} as IPerson;
    return function(key:string,val:number | string){
        obj[key] = val;
        --times == 0 && callback(obj);
    }
}
let fn = after(2,(obj: any)=>{
    console.log(obj);
})
fs.readFile('./age.txt','utf8',(err: any,data: number)=>{
    fn('age',data);
});
//这里会出现不能读取文件的问题,那就通过path.resolve(__dirname, '')获得绝对路径,替换readFile路径就好了
fs.readFile('./name.txt','utf8',(err: any,data: string)=>{
    fn('name',data);
});

铺垫

特点: promise是基于回调形式的,所以要想搞懂原理,首先要清楚一些基本知识:回调函数、高阶函数、柯里化函数呀等, 下面用几个例子分别介绍一下。

切片编程

高阶函数定义: 参数是函数或者返回值是函数的函数是高阶函数,高阶函数最常见的应用模式就是切片编程,即不改变函数原有功能的基础上给函数增加新的功能。

// 切片编程
type Callback = () => void;
type ReturnFn = (...args: number[]) => void;

interface Function {
  before(fn: Callback): ReturnFn
}

Function.prototype.before = function (fn) {
  return (...args) => {
    fn();
    this(...args)
  }
}

function core(a: number, b: number, c: number) {
  console.log(a, b, c);
}

let HigherWithCore = core.before(() => {
  console.log('before');
})

HigherWithCore(2, 5, 6)

柯里化函数

juejin.cn/post/691940…

为什么不使用tsc编译成js文件,不可能一个一个文件编译,因此用rollup打包工具
target: es5 目标语法是es5但是,如果代码中写了proxy,还是不会被编译,因为本身es5就没有

原理剖析

这里附上github地址,具体分析就是每一条的commit记录:

github.com/China-forre…

我们自己实现的promise原理的特点: promise是内部的,我们现在只是模拟实现Promise,那我们模拟过程中注意以下几个点

  • 既然promise能实例化,那么它是一个类
  • 打包后bundle.js文件看到,module.exports = Promise 说明是node语法来导出的
  • 虽然我们书写语法是es语法,但是是给node用的,所以需要在rollup.config.js配置文件中配置format: cjs,这样打包时就会遵循commonjs规范,编译成最终node需要的语法。
  • 如果你自己写的库中的promise需要和别人的promise结合,那么主要逻辑是resolvePromise(github的commit记录中)

还在继续更新,有帮助的可以 一波!

后续附上:promise所有场景面试题,以此检验是否掌握。

  • 简单实现一下promise的all方法
Promise.all = function(values) {
    return new Promise((resolve, reject) => {
        let arr = [];
        let times = 0;
        
        function collectResult(val, key) {
            arr[key] = val;
            if(++times === values.length) {
                resolve(arr);
            }
        }
        
        for(let i = 0; i < values.length; i++) {
            let value = values[i];
            if(value && isPromise(value)) {
                value.then(y => {
                    collectResult(y, i);
                }, reject)
            }else {
                collectResult(value, i);
            }
        }
    })
}

思考题

  • 如何实现node API变成一个promiseAPI