本篇文章通过构建一个简单的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实际上是一个对象,当我们运行下面的代码可以发现返回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,它们的作用分别是
用来收集有数据的时候的回调函数,放在this.promiseChain里,注意这里要返回this 对象才能实现链式调用
用来处理出现的error,注意这里要返回this对象实现链式调用
依次执行then里面收集的回调函数,并且将回调函数的返回值在作为参数传给下一个回调函数
用来处理出现的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);
});