【手写 Promise 源码】第一篇 - Promise 简介

484 阅读6分钟

一,前言

上一篇,完成了 Promise 源码学习的目录;

本篇,主要对 Promise 进行简单的概括介绍;


二,Promise 简介

  • Promise 是一个类或构造函数,是 JS 原生提供的,通过实例化 Promise 完成预期的异步任务处理;
  • Promise 是一个能够获取异步操作消息的对象;译为承诺,表示会在一段时间后返回处理结果;
  • Promise 是一种异步编程的解决方案,被 ES6 列为正式规范,提供了原生 Promise 对象;
  • Promise 能够接收一个异步请求,在提供的 then/catch 方法中拿到异步请求的处理结果;
  • Promise 支持链式调用,通过链式调用的方式能够将异步请求的结果以同步的方式进行处理;

三,Promise 基本使用

使用 Promise 对异步操作进行包装:

// 创建
var promise = new Promise((resolve, reject)=>{
  setTimeout(()=>{
    if("异步请求成功"){
      resolve('ok');
    } else {
      reject('error')
    }
  }, 2000);
});

// 使用
promise.then(data=>{
  console.log(data);
}).catch(error=>{
  console.log(error);
});

从示例中能够看出:

  • Promise 的参数是一个回调函数,其中包含了两个参数:resolve 和 reject;
  • 当异步操作成功时,通过调用 resolve 返回异步操作的成功结果;
  • 当异步操作失败时,通过调用 reject 返回异步操作的失败结果;
  • 异步操作成功时,进入 then 处理后续操作;
  • 异步操作失败时,进入 catch 处理后续操作;(当前示例是这样的,当then函数的第二个回调定义时,也可以捕捉失败操作,这个机制后面会详细解释)

但实际上,除以上示例体现出来的部分,Promise 还具有更多特性,理解并掌握这些特性是用好 Promise 的基础;


四,Promise 和 callback 对比

callback 方式处理异步操作:

function async(callback){
    setTimeout(function(){
        callback("异步操作完成");
    }, 1000);
}

async(function(data){
    console.log(data);
});

Promise 和 callback 的本质区别,就是控制权反转

  • 在 callback 模式下,回调函数的执行控制权在封装层:

    • 业务层将回调函数定义传递给封装层
    • 封装层在任务结束时执行了回调函数;
    • 业务层并没有调用回调函数,甚至连调用的代码都看不到;
  • 在 Promise 模式下,回调函数的执行控制权在业务层:

    • 业务层并没有把回调函数直接传递给封装层(Promise 对象内部);
    • 封装层在任务结束时,并不知道要执行的回调,只能通过 resolve 或 reject 通知到业务层,由业务层在 then() 或 reject() 中控制回调执行;
    • 业务层不仅能看到回调函数的调用代码,还能对其进行修改;

五,Promise 的作用

Promise 可以说(主要)是为了解决程序异步处理而生的;

  • Promise 不仅解决了异步回调的多层嵌套,即“回调地狱”问题;
  • 更重要的,Promise 提供了更完整、更强大的异步解决方案;通过 Promise 构造函数上提供的方法,实现对 Promise 批量操作的支持,如:all、race、any、allSettled 等;

六,Promise 的重要性

目前,Promise 在前端领域的应用中,几乎是无处不在的,已经成为了前端开发中最重要的技能之一;

Promise 也是前端面试的高频考察点:考察方式多样化,考察内容可深可浅;同时,也能够关联出很多实际应用场景;因此,熟练地掌握 promise 是每个前端开发的必备能力;


七,Promise 的兼容性

1,兼容性介绍

Promise 属于 ES6 规范内容,浏览器支持情况可查看 Can I useMDN

Can I use.png

MDN.png

  • Chrome 浏览器 - 支持
  • 360 浏览器 - 兼容模式下支持
  • IE 内核浏览器 - 不支持

2,解决方案

  • bluebird.js 在项目中使用 Promsie 对象,可以使用第三方插件 bluebird.js;

bluebird 对 ES6 原生 Promise 进行封装,解决了浏览器兼容性问题;

  • es6-promise 使用 es6-promise 也可以解决 IE 下不支持 Promise 的问题;

八,Promise 使用场景

JS 的异步处理可以说是无处不在的,比如:nodejs 和小程序所提供的 API 都是基于回调的;

而这也就导致了开发中时常出现“回调地狱”的坏味道(团队 CodeReview 关注点):

const fs = require('fs');

fs.readFile('./a.txt','utf8',(err,data)=>{
  if(err) return err
  console.log(data)
  fs.readFile('./b.txt','utf8',(err,data)=>{
    if(err) return err
    console.log(data)
    fs.readFile('./c.txt','utf8',(err,data)=>{
      if(err) return err
      console.log(data)
      fs.readFile('./d.txt','utf8',(err,data)=>{
        if(err) return err
        console.log(data)
        // ...回调地狱
      })
    })
  })
})

为了解决类似这种异步操作带来的问题,使代码编写更加优雅且更具可读性,Promise 作为前端异步编程的解决方案诞生了;

上边的代码使用 Promise 进行包装处理,如下:

const fs = require('fs');

// 使用 Promise 封装处理 fs.readFile
function readFile(filePath, encoding) {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, encoding, (err, data) => {
      if (err) return reject(err) //失败
      resolve(data) // 成功
    })
  });
}

// 使用方式
readFile('./a.txt','utf8').then((data) => {
  console.log(data)
  return readFile('./b.txt','utf8')
}).then((data) => {
  console.log(data)
  return readFile('./c.txt','utf8')
}).then((data) => {
  console.log(data)
  return readFile('./d.txt','utf8')
}).then((data) => {
  console.log(data)
})

这种将异步回调包装成为 Promise 的处理方式也叫 Promisify,常用于 nodejs、小程序场景,使异步代码更简洁;

  • 备注: 使用类库时仔细查看官方说明,目前很多类库都支持返回 Promise 实例,应尽量避免在外部进行重复包装;部分类库既支持 callback 也支持 Promise 形式;

九,Promise 优缺点

  • 优点

    • 优化异步代码,解决“回调地狱”问题,增强代码的可读性、可维护性;
    • Promise 对象提供统一的接口,使得控制异步操作更加容易;
    • 通过 catch 方法对异常进行统一的捕获处理;
  • 缺点

    • 无法取消 Promise,一旦创建它就会立即执行,无法中途取消;
    • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部;
    • 当处于 Pending 状态时,是无法得知目前进展到哪一个阶段(刚刚开始还是即将完成);

十,结尾

本篇,对 Promise 进行了简单介绍,主要涉及以下几个点:

  • Promise 简介和基本使用;
  • Promise 和 callback 对比;
  • Promise 的重要性和作用;
  • Promise 使用场景:Promisify 封装;
  • Promise 的优缺点、兼容性;

备注:本篇的定位仅仅是简单的认识和了解 Promise,并未涉及深入的介绍和分析,比如:Promise 的状态、执行顺序、链式调用、api 使用、异常处理等;

下一篇,将结合示例对 Promise 进行相关功能介绍与特性分析;


维护日志

  • 20211019
    • 更新部分示例代码,更符合相关知识点;
    • 添加 promise 兼容性部分;
    • 更新结尾部分、文章摘要;
  • 20211024
    • 扩充部分内容;
  • 20211025
    • 重新梳理大纲,调整一二级标题、扩充相关内容;
    • 修改文章标题、摘要;