异步任务按顺序处理

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);
复制代码

image.png
(绿色部分为等待时间,蓝色部分为下载时间)

从这张图可以看出在任何时刻,至多有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一个。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享