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 条评论
登录 后参与评论

相关文章

来自专栏C#

C#二进制流的序列化和反序列化

1 public class BinaryHelper 2 { 3 /// <summary> 4 /...

30070
来自专栏DannyHoo的专栏

iOS中防止数组越界之后发生崩溃

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

34410
来自专栏ShaoYL

IOS开发系列—Objective-C之Foundation框架

23460
来自专栏c#开发者

一种自动的将自定义类序列化为JSON的方法

最近因为项目需求,需要将一些自定义的类序列化为JSON,网上有很多好用的第三方序列化工具,但都只能自动序列化一些基本类型,如NSNumber,NSString与...

39170
来自专栏菩提树下的杨过

objective-C中的Class(类类型),Selector(选择器SEL),函数指针(IMP)

今天在园子里看到了一篇牛文“Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数...

25650
来自专栏一“技”之长

Objective-C中runtime机制的应用

        Objective-C是一种动态语言,所谓动态语言,是在程序执行时动态的确定变量类型,执行变量类型对应的方法的。因此,在Object-C中常用字...

9150
来自专栏ios 技术积累

ios ARC中如何判断一个对象释放了

NSHashTable效仿了NSSet(NSMutableSet),但提供了比NSSet更多的操作选项,尤其是在对弱引用关系的支持上,NSHashTable在对...

37520
来自专栏進无尽的文章

编码篇 - NSInvocation的简单使用

在认识 NSInvocation 之前,iOS开发中我们一般会使用以下两种方式去调用一个方法

16820
来自专栏码生

ios OC 获取所有属性、变量 class_getProperty class_copyPropertyList class_copyIvarList

28250
来自专栏青玉伏案

Objective-C中的内存管理

        在编程语言中是少不了对内存的管理的,内存对于计算机来说是宝贵的资源,所以对使用不到的资源进行回收是很有必要的。OC中使用引用计数和垃圾回收来管理...

19390

扫码关注云+社区

领取腾讯云代金券