本篇文章通过构建一个简单的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/gitconnect...


幽_月
316 声望72 粉丝