1
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);
})
解析
resolve
函数将Promise
对象的状态从“未完成”变为“成功”(即从pending
变为resolved
),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject
函数将Promise
对象的状态从“未完成”变为“失败”(即从pending
变为rejected
),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
而一旦状态改变,就不会再变。所以代码中的reject('error');
不会有作用。
Promise
只能resolve
一次,剩下的调用都会被忽略。
所以 第二次的resolve('success2');
也不会有作用。
答案
then: success1
2
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
红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用Promse
实现)三个亮灯函数已经存在:
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
解析
红灯三秒亮一次,绿灯一秒亮一次,黄灯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();
4
实现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]
解析
首先ajax1
、ajax2
、ajax3
都是函数,只是这些函数执行后会返回一个Promise
,按题目的要求我们只要顺序执行这三个函数就好了,然后把结果放到data
中,但是这些函数里都是异步操作,想要按顺序执行,然后输出 1,2,3并没有那么简单,看个例子。
function A() {
setTimeout(function () {
console.log('a');
}, 3000);
}
function B() {
setTimeout(function () {
console.log('b');
}, 1000);
}
A();
B();
// b
// a
例子中我们是按顺序执行的A
,B
但是输出的结果却是b
,a
对于这些异步函数来说,并不会按顺序执行完一个,再执行后一个。
这道题就是考用Promise
控制异步流程,我们要想办法,让这些函数,一个执行完之后,再执行下一个,看答案吧。
答案
// 保存数组中的函数执行后的结果
var data = [];
// Promise.resolve方法调用时不带参数,直接返回一个resolved状态的 Promise 对象。
var sequence = Promise.resolve();
ajaxArray.forEach(function (item) {
// 第一次的 then 方法用来执行数组中的每个函数,
// 第二次的 then 方法接受数组中的函数执行后返回的结果,
// 并把结果添加到 data 中,然后把 data 返回。
sequence = sequence.then(item).then(function (res) {
data.push(res);
return data;
});
})
// 遍历结束后,返回一个 Promise,也就是 sequence, 他的 [[PromiseValue]] 值就是 data,
// 而 data(保存数组中的函数执行后的结果) 也会作为参数,传入下次调用的 then 方法中。
return sequence;
5
以下代码最后输出什么?
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);
解析
这道题就其实和Promise
的关系不太大,主要是需要理解 JS执行机制,才能很好的解决这道题
第一轮事件循环
先执行宏任务,主script
,new Promise
立即执行,输出【3】,
执行p
这个new Promise
操作,输出【7】,
发现setTimeout
,将回调放入下一轮任务队列(Event Queue
),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
6
有8个图片资源的url
,已经存储在数组urls
中(即urls = ['http://example.com/1.jpg', …., 'http://example.com/8.jpg']
),而且已经有一个函数function loadImg
,输入一个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
})
};
function limitLoad(urls, handler, limit) {
// 对数组做一个拷贝
const sequence = [].concat(urls)
let promises = [];
//并发请求到最大数
promises = sequence.splice(0, limit).map((url, index) => {
// 这里返回的 index 是任务在 promises 的脚标,用于在 Promise.race 之后找到完成的任务脚标
return handler(url).then(() => {
return index
});
});
// 利用数组的 reduce 方法来以队列的形式执行
return sequence.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(sequence[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);
})
*/