Promise的简单实现

本篇文章通过构建一个简单的Promise对象来了解如何做到异步获得数据。

使用方法

const fetch = function(url) {
  return new Promise((resolve, reject) => {
    request((error, apiResponse) => {
      if (error) {
         //返回为error的时候,调用reject函数
        reject(error)
      }
      // 有数据返回的时候,调用resolve
      resolve(apiResponse)
    })
  })
}

这个fetch()的方法返回了一个Promise对象,接着我们就可以用then来对获得的数据进行处理,catch来捕获可能的错误。

Promise的简单实现

首先,我们要知道Promise实际上是一个对象,当我们运行下面的代码可以发现返回true。

console.log(typeof new Promise((resolve, reject) => {}) === 'object') // true

接着要构建一个Promise类,来生成Promise Object。

思路:

在constructor 里面传入executionFunction, 然后将onResolve,onReject映射到里面,then主要做一件事情,就是将onResolve的函数收集起来,在this.onResolve里面一起调用,每次返回的值都覆盖前一次的。

说的什么玩意儿,眼见为实下面来看一下代码:

class PromiseSimple{
    constructor(executionFunction) {
        this.promiseChain = [];
        this.onResolve = this.onResolve.bind(this)
        this.onReject = this.onReject.bind(this)
        this.handleError = () => {}
        // 外界传入的函数我们将其定义为executionFunction,将里面
        // 的onResolve onReject 映射到this.onResolve, this.onReject
        executionFunction(this.onResolve, this.onReject)
    }
    then(onResolve) {
        // 收集状态成功的时候的回调函数
        this.promiseChain.push(onResolve)
        return this
    } 
    catch(onReject) {
        this.handleError = onReject
        return this
    }
    onResolve(value) {
    var storedValue = value;
    try {
        // 在resolve里面执行
        this.promiseChain.forEach((executePromise) => {
            storedValue = executePromise(storedValue)
        })
    } catch(error){
        this.promiseChain = [];
        this.onReject(error)
    }
    }
    onReject(error) {
        this.handleError(error)
    }
}

梳理一下,其实只要记住其中两点:

1、这个对象有四个方法then catch onResolve onReject,它们的作用分别是

then

用来收集有数据的时候的回调函数,放在this.promiseChain里,注意这里要返回this 对象才能实现链式调用

catch

用来处理出现的error,注意这里要返回this对象实现链式调用

onResolve

依次执行then里面收集的回调函数,并且将回调函数的返回值在作为参数传给下一个回调函数

onReject

用来处理出现的error

2、then catch 必须要返回this,才能实现链式调用

这样我们一个简单的Promise 对象就做好了

下面可以用这个来玩一玩

class PromiseSimple {
  constructor(executionFunction) {
    this.promiseChain = [];
    this.handleError = () => {};

    this.onResolve = this.onResolve.bind(this);
    this.onReject = this.onReject.bind(this);

    executionFunction(this.onResolve, this.onReject);
  }

  then(onResolve) {
    this.promiseChain.push(onResolve);

    return this;
  }

  catch(handleError) {
    this.handleError = handleError;

    return this;
  }

  onResolve(value) {
    let storedValue = value;

    try {
      this.promiseChain.forEach((nextFunction) => {
         storedValue = nextFunction(storedValue);
      });
    } catch (error) {
      this.promiseChain = [];

      this.onReject(error);
    }
  }

  onReject(error) {
    this.handleError(error);
  }
}

fakeApiBackend = () => {
  const user = {
    username: 'treyhuffine',
    favoriteNumber: 42,
    profile: 'https://gitconnected.com/treyhuffine'
  };

  // Introduce a randomizer to simulate the
  // the probability of encountering an error
  if (Math.random() > .05) {
    return user;
  } else {
    const error = {
      statusCode: 404,
      message: 'Could not find user',
      error: 'Not Found',
    };

    return error;
  }
};

// Assume this is your AJAX library. Almost all newer
// ones return a Promise Object
const makeApiCall = () => {
  return new PromiseSimple((resolve, reject) => {
    // Use a timeout to simulate the network delay waiting for the response.
    // This is THE reason you use a promise. It waits for the API to respond
    // and after received, it executes code in the `then()` blocks in order.
    // If it executed is immediately, there would be no data.
    setTimeout(() => {
      const apiResponse = fakeApiBackend();

      if (apiResponse instanceof  Error) {
        reject(apiResponse);
      } else {
        resolve(apiResponse);
      }
    }, 5000);
  });
};

makeApiCall()
  .then((user) => {
    console.log('In the first .then()');

    return user;
  })
  .then((user) => {
    console.log(`User ${user.username}'s favorite number is ${user.favoriteNumber}`);

    return user;
  })
  .then((user) => {
    console.log('The previous .then() told you the favoriteNumber')

    return user.profile;
  })
  .then((profile) => {
    console.log(`The profile URL is ${profile}`);
  })
  .then(() => {
    console.log('This is the last then()');
  })
  .catch((error) => {
    console.log(error.message);
  });

参考文档: https://medium.com/gitconnected/understand-javascript-promises-by-building-a-promise-from-scratch-84c0fd855720

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据结构与算法

树上莫队算法

1063
来自专栏web前端教室

javasciprt 排序 之 冒泡排序

冒泡排序哈,最简单,也最慢,据说长度小于7位是最佳的。具体是不是不知道,我没测过。 算法这里大家不要学我,我学算法和排序,完全是不求甚解的学法。就是我只要知道...

1808
来自专栏逆向技术

16位汇编语言第二讲系统调用原理,以及各个寄存器详解

   16位汇编语言第二讲系统调用原理,以及各个寄存器详解 昨天已将简单的写了一下汇编代码,并且执行了第一个显示到屏幕的helloworld 问题?   hel...

1680
来自专栏编程

PLC编程优化方法,让程序运行提速!

PLC、DCS、仪器仪表、电气技术资料,一网打尽 通过本方法优化可以极大的减少程序语句数,使PLC程序更简洁、可读性更好,由于不需要做耗时的类型转换,程序运行效...

1959
来自专栏前端知识分享

第193天:js---Math+Error+Number+Object总结

422
来自专栏大内老A

.NET Core采用的全新配置系统[4]: “Options模式”下各种类型的Options对象是如何绑定的?

旨在生成Options对象的配置绑定实现在IConfiguration接口的扩展方法Bind上。配置绑定的目标类型可以是一个简单的基元类型,也可以是一个自定义数...

1787
来自专栏技术之路

【转】Go Interface 源码剖析

源网址:http://legendtkl.com/2017/07/01/golang-interface-implement/

662
来自专栏数据结构与算法

cf550C. Divisibility by Eight(结论)

设$x = 10000 * a_i + 1000 * a_{i - 1} + \dots$

532
来自专栏移动端周边技术扩展

数据分析函数

611
来自专栏华仔的技术笔记

tabviewcell侧滑删除出现的异常

31510

扫码关注云+社区