本篇文章通过构建一个简单的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 Objectconst 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); });
参考文档: