最近面试被问到了手写
Promise .all
与Promise.race
,奈何没有自己实现过,只能阿巴阿巴
面完之后,冷静下来思考了该如何实现,并把他写了下来(在实现过程中确实收获不少,让我对这两个方法有了更深的理解)
这里用到了上次写得promiseAjax,用于测试
function promiseAjax(url, type, data, timeout = 8000) {
return new Promise((resolve, reject) => {
type = type.toLocaleLowerCase()
const xhr = new XMLHttpRequest()
xhr.timeout = timeout
if (type === 'get') {
if (data) {
url += '?'
for (let key in data) {
url += (key + '=' + data[key] + '&')
}
url = url.substring(0, url.length - 1)
}
xhr.open(type, url)
xhr.send(null)
}
if (type === 'post') {
xhr.open(type, url)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
if (data) {
xhr.send(data)
} else {
xhr.send(null)
}
}
let timer = setTimeout(() => {
reject('请求超时了')
throw new Error('请求超时')
}, timeout)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let result = JSON.parse(xhr.responseText)
clearTimeout(timer)
resolve(result)
}
}
}
xhr.onerror = function(err) {
reject('请求失败,' + err)
}
})
}
好了,接下来是实现代码及过程
第一步:函数基本结构
const MyPromise = (function() {
const _promise = {
all: _all,
race: _race,
}
function _all(promiseArr) {
}
function _race(promiseArr) {
}
// 判断一个对象是否是Promise
function isPromise(p) {
return !!p && (typeof p === 'object' || typeof p === 'function') && typeof p.then === 'function'
}
return _promise
})()
实现_all方法
Promise.all功能
- 它接受一个数组作为参数。
- 数组可以是Promise对象,也可以是其它值,只有Promise会等待状态改变。
- 当所有的子Promise都完成,该Promise完成,返回值是全部值的数组。
- 如果有任何一个失败,该Promise失败,返回值是第一个失败的Promise的结果。
参考来源
主要思路:
- 新建一个Promise实例(明确返回的结果是一个Promise)
- 新建一个用于保存回调结果的数组resultArr
- 遍历传入的Promise数组
- 判断当前项是否为Promise类型,若不是,直接将结果存入resultArr(这里需要传入一个当前项下标,用于按传入的数组位置排序)
- 若是,则使用.then取得回调结果的值,若resultArr的长度等于传入的Promise数组的长度,则resolve(resultArr)
- 使用.catch回调取得失败的值,然后直接reject
/**
* 实现功能:传入的Promise数组所有状态都为Fulfilled时,返回所有Promise的结果数组,
* 若有一个状态为Reject,则提前reject
* */
function _all(promiseArr) {
// 新建一个Promise实例(明确返回的结果是一个Promise)
return new Promise((resolve, reject) => {
// 保存结果数组
let resultArr = []
// 循环遍历每一个Promise
promiseArr.forEach((promiseItem, index) => {
// 判断是否是Promise
if (isPromise(promiseItem)) {
// 如果Promise成功
promiseItem.then(result => {
pushSuccessResult(result, index)
})
// 当有一个Promise失败则直接reject
.catch(err => {
reject(err)
})
} else {
// 如果当前项不是Promise对象,直接存入
pushSuccessResult(promiseItem, index)
}
})
// 存入结果数组
function pushSuccessResult(result, index) {
resultArr.push({
index,
result
})
if (resultArr.length === promiseArr.length) {
// 按传入的数组位置进行排序
resultArr.sort((a, b) => a.index - b.index)
resolve(resultArr.map(item => item.result))
}
}
})
}
这里需要注意的是保持 返回的结果数组中各项的位置 与 传入数组各项的位置 一致
实现_race方法
只要有一个Promise触发回调,则resolve或reject
主要思路:
- 新建一个Promise实例(明确返回的结果是一个Promise)
- 遍历传入的Promise数组
- 判断当前项是不是Promise,若不是,则直接resolve该项
- 若是,取得该项的回调,返回结果
// 实现功能:只要有一个Promise触发回调,则resolve或reject
function _race(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(promiseItem => {
if (isPromise(promiseItem)) {
promiseItem.then(result => {
resolve(result)
})
.catch(err => {
reject(err)
})
} else {
resolve(promiseItem)
}
})
})
}
测试
let posts = promiseAjax('http://jsonplaceholder.typicode.com/posts', 'get', null)
let comments = promiseAjax('http://jsonplaceholder.typicode.com/comments', 'get', null)
let albums = promiseAjax('http://jsonplaceholder.typicode.com/albums', 'get', null)
MyPromise.all([3, comments, posts], 2000).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
MyPromise.race([albums, comments]).then(res => {
console.log(res)
})
.catch(err => {
console.log(err);
})