四、相关面试题
1. 多个 .catch
var p = new Promise((resolve, reject) => {
reject(Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))
以上代码的输出将会是什么?
打印两次 The Fails!
解析:我们使用构造函数方法创建一个 Promise,并通过
reject
回调立即触发错误。然后.catch
工作方式类似于 DOM 的.addEventListener(event,callback)
或 Event Emitter 的.on(event,callback)
,其中可以添加多个回调。每个都用同样的参数进行调用。
2. 多个 .catch
var p = new Promise((resolve, reject) => {
return Promise.reject(Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))
以上代码的输出将会是什么?
解析:
使用 Promise 构造函数时,必须调用
resolve()
或reject()
回调。 Promise 构造函数不使用你的返回值,因此实际上不会再收到由Promise.reject()
创建的其他 Promise。在
Promise.reject()
之后没有.catch
时,答案是UnhandledPromiseRejectionWarning
。
3、链接 .then
和 .catch
var p = new Promise((resolve, reject) => {
reject(Error('The Fails!'))
})
.catch(error => console.log(error))
.then(error => console.log(error))
以上代码的输出将会是什么?
打印错误和
undefined
解析:
当链接
.then
和.catch
时,将它们视为一系列步骤会很有帮助。每个.then
都接收前一个.then
返回的值作为其参数。但是,如果你的 “step” 遇到错误,则任何后续的.then
“ steps” 都将被跳过,直到遇到.catch
。如果要覆盖错误,你要做的就是返回一个非错误值。可以通过任何随后的.then
访问。提示:
console.log()
总是返回undefined
。
4. 链接 .catch
var p = new Promise((resolve, reject) => {
reject(Error('The Fails!'))
})
.catch(error => console.log(error.message))
.catch(error => console.log(error.message))
以上代码的输出将会是什么?
The Fails!
解析
当链接
.catch
时,每个仅处理先前的.then
或`.catch
“步骤” 中引发的错误。在此例中,第一个.catch
返回console.log
,只能通过在两个.catch
之后添加.then()
来访问。
5. 多个 .catch
new Promise((resolve, reject) => {
resolve('Success!')
})
.then(() => {
throw Error('Oh noes!')
})
.catch(error => {
return "actually, that worked"
})
.catch(error => console.log(error.message))
以上代码的输出将会是什么?
不打印任何内容
提示:
.catch
可以简单地通过返回一个常规值来忽略(或覆盖)错误。该技巧仅在随后的
.then
接收该值时有效。
6. .then
之间的流程
Promise.resolve('Success!')
.then(data => {
return data.toUpperCase()
})
.then(data => {
console.log(data)
})
以上代码的输出将会是什么?
SUCCESS!
提示:.then依次传递数据,从
return value
到下一个.then(value => /* handle value */)
。为了将值传递给下一个
.then
,return
是关键。
7. .then
之间的流程
Promise.resolve('Success!')
.then(data => {
return data.toUpperCase()
})
.then(data => {
console.log(data)
return data
})
.then(console.log)
以上代码的输出将会是什么?
打印 "SUCCESS!" 和 "SUCCESS!"
解析:
有两个
console.log
调用将被调用。
8. .then
之间的流程
Promise.resolve('Success!')
.then(data => {
data.toUpperCase()
})
.then(data => {
console.log(data)
})
以上代码的输出将会是什么?
打印
undefined
提示:
.then
依次传递数据,从返回值到下一个.then(value => /* handle value */)
。为了将值传递给下一个
.then
,return
是关键。
9. .then
和 .catch
之间的流程
Promise.resolve('Success!')
.then(() => {
throw Error('Oh noes!')
})
.catch(error => {
return 'actually, that worked'
})
.then(data => {
throw Error('The fails!')
})
.catch(error => console.log(error.message))
以上代码的输出将会是什么?
The fails!
10、基础输出题
以下代码最后输出什么?
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
1 2 4 3
记住 new Promise 里的参数函数,是同步被执行的,故而先输出 1,2,resolve 后还需要等待进入下一个事件循环。then 把参数函数推入微任务队列,并不直接执行。输出 4,接着事件循环进入下一轮,输出 3.
以下代码最后输出什么?
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1);
}, 3000)
})
// 打印结果是??
promise
.then(() => {
return Promise.resolve(2)
})
.then(n => {
console.log(n)
})
// 打印结果是??
promise
.then(() => {
return 2
})
.then(n => {
console.log(n)
})
// 打印结果是??
promise.then(2).then(n => {
console.log(n)
})
输出2。Promise.resolve 就是一个 Promise 对象就相当于返回了一个新的 Promise 对象。然后在下一个事件循环里才会去执行 then
输出2。和上一点不一样的是,它不用等下一个事件循环。
输出1。then 和 catch 期望接收函数做参数,如果非函数就会发生 Promise 穿透现象,打印的是上一个 Promise 的返回。
以下代码最后输出什么?
const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise
.then((res) => {
console.log('then: ', res);
})
.catch((err) => {
console.log('catch: ', err);
});
打印:then: success1
牢牢记住 Promise 对象的状态只能被转移一次,resolve('success1') 时状态转移到了 fullfilled 。后面 reject 就调用无效了,因为状态已经不是 pending
以下代码最后输出什么?
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
没有抛出错误和异常,只是 return 了一个对象,哪怕他是 Error 对象,那自然也是正常走 then 的链式调用下去了,不会触发 catch
以下代码最后输出什么?
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
})
promise.then(() => {
console.log(3);
})
console.log(4);
首先Promise新建后立即执行,所以会先输出1,2,而Promise.then()内部的代码在当次事件循环的结尾立即执行,所以会先输出4,最后输出3.
答案:1 2 4 3
以下代码最后输出什么?
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
Promise.resolve方法的参数如果是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为resolved,Promise.resolve方法的参数,会同时传给回调函数。
then方法接受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为then(null),这就会导致前一个Promise的结果会传递下面。
答案 1
红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现)三个亮灯函数已经存在:
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
红灯3秒亮一次,绿灯1秒亮一次 ,黄灯2秒亮一次,意思就是3秒执行一次red函数,2秒执行一次green函数,1秒执行一次yellow函数,不断交替重复亮灯,意思就是按照这个顺序一直执行这3个函数,这步可以利用递归来实现。
function red() { console.log('red'); } function green() { console.log('green'); } function yellow() { console.log('yellow'); } var light = function (timmer, cb) { return new Promise(function (resolve, reject) { setTimeout(function () { cb(); resolve(); }, timmer); }); }; var step = function () { Promise.resolve().then(function () { return light(3000, red); }).then(function () { return light(2000, green); }).then(function () { return light(1000, yellow); }).then(function () { step(); }); } step();
实现mergePromise函数,把传进去的数组按顺序先后执行,并且把返回的数据先后放到数组data中。
const timeout = ms => new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
});
const ajax1 = () => timeout(2000).then(() => {
console.log('1');
return 1;
});
const ajax2 = () => timeout(1000).then(() => {
console.log('2');
return 2;
});
const ajax3 = () => timeout(2000).then(() => {
console.log('3');
return 3;
});
const mergePromise = ajaxArray => {
// 在这里实现你的代码
};
mergePromise([ajax1, ajax2, ajax3]).then(data => {
console.log('done');
console.log(data); // data 为 [1, 2, 3]
});
// 要求分别输出
// 1
// 2
// 3
// done
// [1, 2, 3]
因为对于异步函数来说,并不会按顺序执行完一个,再执行后一个。
这道题主要考察用Promise控制异步流程,让这些函数一个执行完,再执行下一个。const timeout = ms => new Promise((resolve, reject) => { setTimeout(() => { resolve() }, ms) }) const ajax1 = () => timeout(2000).then(() => { console.log('1') return 1 }) const ajax2 = () => timeout(1000).then(() => { console.log('2') return 2 }) const ajax3 = () => timeout(2000).then(() => { console.log('3') return 3 }) const mergePromise = ajaxArray => { // 保存数组中的函数执行后的结果 var arr = [] // Promise.resolve方法调用时不带参数,直接返回一个resolved状态的 Promise 对象 var resolved = Promise.resolve() ajaxArray.forEach(item => { resolved = resolved.then(item).then(res => { // 第一次的 then 方法用来执行数组中的每个函数, // 第二次的 then 方法接受数组中的函数执行后返回的结果, // 并把结果添加到 data 中,然后把 data 返回。 arr.push(res) return arr }) }) // 遍历结束后,返回一个 Promise,也就是 resolved, 他的 [[PromiseValue]] 值就是 arr // 而 data(保存数组中的函数执行后的结果) 也会作为参数,传入下次调用的 then 方法中。 return resolved } mergePromise([ajax1, ajax2, ajax3]).then(data => { console.log('done') console.log(data) }) // 要求分别输出 // 1 // 2 // 3 // done // [1, 2, 3]
以下代码最后输出什么?
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
这道题主要理解js执行机制
第一轮事件循环
先执行宏任务,主script,new Promise立即执行,输出 3,
执行p这个new Promise操作,输出 7,
发现setTimeout,将回调函数放入下一轮任务队列(Event Quene),p的then,暂且命名为then1,放入微任务队列,且first也有then,命名为then2,放入微任务队列。执行console.log(4),输出 4,宏任务执行结束。
再执行微任务,执行then1,输出 1,
执行then2,输出 2.
第一轮事件循环结束,开始执行第二轮。
第二轮事件循环
先执行宏任务里面的,也就是setTimeout的回调,输出 5.
resolve(6)不会生效,因为p的Promise状态一旦改变就不会再变化了。
答案 3 7 4 1 2 5
有8个图片资源的url,已经存储在数组urls中(即urls=['http://example.com/1.jpg',...,'http:''example.com/8.jpg']),而且已经有一个函数function loading,输入一个url链接,返回一个Promise,该Promise在图片下载完成的时候resolve,下载失败则reject。
但有一个要求,任何时刻同时下载的链接数量不可以超过3个。
请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。
var urls = ['https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg', 'https://www.kkkk1000.com/images/getImgData/gray.gif', 'https://www.kkkk1000.com/images/getImgData/Particle.gif', 'https://www.kkkk1000.com/images/getImgData/arithmetic.png', 'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif', 'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg', 'https://www.kkkk1000.com/images/getImgData/arithmetic.gif', 'https://www.kkkk1000.com/images/wxQrCode2.png'];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function () {
console.log('一张图片加载完成');
resolve();
}
img.onerror = reject
img.src = url
})
};
解析
题目的意思是需要先并发请求3张图片,当一张图片加载完成后,又会继续发起一张图片的请求,让并发数保持在3个,直到需要加载的图片都全部发起请求。
用Promise来实现就是,先并发请求3个图片资源,这样可以得到3个Promise,组成一个数组promises,然后不断调用Promise.race来返回最快改变状态的Promise,然后从数组promises中删掉这个Promise对象,再加入一个新的Promise,直到全部的url被取完,最后再使用Promise.all来处理一遍数组promises中没有改变状态的Promise
答案:var urls = [ 'https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg', 'https://www.kkkk1000.com/images/getImgData/gray.gif', 'https://www.kkkk1000.com/images/getImgData/Particle.gif', 'https://www.kkkk1000.com/images/getImgData/arithmetic.png', 'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif', 'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg', 'https://www.kkkk1000.com/images/getImgData/arithmetic.gif', 'https://www.kkkk1000.com/images/wxQrCode2.png' ] function loadImg(url) { return new Promise((resolve, reject) => { const img = new Image() img.onload = function() { console.log('一张图片加载完成') resolve() } img.onerror = reject img.src = url console.log(img.src) }) } function limitLoad(urls, handler, limit) { // 对数组做一个拷贝 const urlArr = [].concat(urls) let promises = [] //并发请求到最大数 promises = urlArr.splice(0, limit).map((url, index) => { // 这里返回的 index 是任务在 promises 的脚标,用于在 Promise.race 之后找到完成的任务脚标 return handler(url).then(() => { console.log(index) return index }) }) console.log(promises) // 利用数组的 reduce 方法来以队列的形式执行 last? return urlArr .reduce((last, url, currentIndex) => { return last .then(() => { // 返回最快改变状态的 Promise return Promise.race(promises) }) .catch(err => { // 这里的 catch 不仅用来捕获 前面 then 方法抛出的错误 // 更重要的是防止中断整个链式调用 console.error(err) }) .then(res => { // 用新的 Promise 替换掉最快改变状态的 Promise promises[res] = handler(urlArr[currentIndex]).then(() => { return res }) }) }, Promise.resolve()) .then(() => { return Promise.all(promises) }) } limitLoad(urls, loadImg, 3) // 因为 limitLoad 函数也返回一个 Promise,所以当 所有图片加载完成后,可以继续链式调用 // limitLoad(urls, loadImg, 3).then(() => { // console.log('所有图片加载完成'); // }).catch(err => { // console.error(err); // })
封装一个异步加载图片的方法
function loadImgAsync(url) { return new Promise(function(resolve, reject) { var img = new Image() img.onload = function() { resolve(img) } img.onerror = function() { reject(new Error('无法加载该路径图片' + url)) } img.src = url }) }