1.实现每隔1s输出一个元素
/**
* 实现每隔1s输出一个元素
*/
const arr = [1, 2, 3, 4, 5];
/**
* 1. async/await实现
*/
async function foo(arr) {
for (const el of arr) {
console.log(el);
// await会一直阻塞,直到后面的Promise执行resolve
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
foo(arr);
/**
* 2. Promise实现
*/
// 手动链式调用
Promise.resolve(1).then(data => {
console.log(data);
return new Promise(resolve=> setTimeout(()=>resolve(2),1000));
}).then(data => {
console.log(data);
return new Promise(resolve=> setTimeout(()=>resolve(3),1000));
}).then(data => {
console.log(data);
return new Promise(resolve=> setTimeout(()=>resolve(4),1000));
}).then(data => {
console.log(data);
})
// 用reduce重写如下,本质上与上面一样
arr.reduce((prev,cur)=> {
return prev.then(()=> {
console.log(cur);
return new Promise(resolve=> {
setTimeout(resolve,1000);
});
});
}, Promise.resolve());
/**
* 3. Generator实现
*/
function *gen(arr) {
for (const el of arr) {
yield delay(el);
}
}
const g = gen(arr);
g.next();
function delay(el) {
console.log(el);
setTimeout(()=> {
g.next();
},1000);
}
复制代码
2.实现限制并发数的图片下载
const urls = [
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting1.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting2.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting3.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting4.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting5.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn6.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn7.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn8.png",
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
console.log(`图片${url}加载完成`);
resolve();
};
img.onerror = function() {
reject(new Error('Could not load image at' + url));
};
img.src = url;
});
};
function limitLoad(urls, handler, limit) {
// ...实现代码
const sequence = urls.slice();
const poll = sequence.splice(0,limit).map((url,idx)=> {
return handler(url).then(()=>idx);
});
sequence.reduce((prev,url)=> {
return prev.then(()=> {
return new Promise(resolve=> {
Promise.race(poll).then(fastestIdx=> {
poll[fastestIdx] = handler(url).then(()=>fastestIdx);
resolve();
})
})
});
},Promise.resolve());
}
limitLoad(urls,loadImg,3);
复制代码
(绿色部分为等待时间,蓝色部分为下载时间)
从这张图可以看出在任何时刻,至多有3张图片在下载。
说明
Promise.resolve().then(()=> {
return 1;
}).then(value=> {
console.log('value ', value);
throw 'error';
}).catch(reason=> {
console.log('reason ', reason);
return 2;
}).then(value=> {
console.log('value ',value);
})
// 相当于
Promise.resolve().then(()=> {
return new Promise(resolve=> resolve(1))
}).then(value=> {
console.log('value ',value);
return new Promise((resolve,reject)=> {
reject('error');
});
}).catch(reason=> {
console.log('reason ',reason);
return new Promise(resolve=> resolve(2));
}).then(value=> {
console.log('value ',value);
})
复制代码
即每个then会默认返回一个新的Promsie对象供下一个then使用。如果then中直接return一个值,会默认将这个值封装进一个Promise对象中,然后在该对象中resolve这个值。也就是说对于then的链式调用,下一个then的执行必须要等到上一个then中return的Promise对象先resolve了。我们上面两个程序的Promise解决方案都是基于这一点完成的。catch同理。
所谓的reduce,其实就是Promise.resolve().then().then()…..then(),然后我们在每个then中return一个new出来的Promise对象。
对于第一个程序,要求每隔1秒再输出下一个数。那么我们在每个then中new一个Promise对象,然后在该对象中使用setTimeout令1秒过后再resolve,从而过了1秒才会进入下一个then。
对于第二个程序,加了限制条件,要求最多同时有3个Promise对象在执行,且当其中某一个完成了后面的马上跟上。同样的,我们搞一个长度为3的poll,里面先把前三个url放进去下载。然后使用then链式调用,对于每个then,我们返回的Promise对象中,使用Promise.race获取第一个下载完成的图片在poll中的index,然后把那个位置用新的Promise对象替换,说明有一个完成了开始下载新的图片了,再resolve进入下一个then,重复前面的操作。由于Promise.race会阻塞着直到里面有一个Promise对象resolve了才会进入Promise.race([xxx]).then(),然后在这个then中我们resolve进入下一个大的then。也就是说,后面所有操作都会等待Promise.race([xxx])中的xxx里resolve一个。