上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
12下一页
返回列表 发新帖

怎么用 Promise 让所有 GM_xmlhttpRequest 完成再进行下一步操作?

[复制链接]
  • TA的每日心情

    2024-5-10 14:44
  • 签到天数: 23 天

    [LV.4]偶尔看看III

    4

    主题

    70

    回帖

    64

    积分

    初级工程师

    积分
    64

    油中3周年挑战者 lv1

    发表于 2022-1-24 07:22:36 | 显示全部楼层 | 阅读模式
    悬赏15油猫币已解决
    本帖最后由 ThisAV 于 2022-1-25 14:04 编辑

    https://www.xiurenji.net/XiuRen/5409.html

    1. //获取图片生成
    2.                     let currentPageID=location.href.match(/(\d+)(?:_\d+)?.html/)[1],
    3.                         pageMax=$('.page>a:last').text()=='下页'?$('.page>a:last').prev().text():$('.page>a:last').text();
    4.                     let ImgList=[];

    5.                     async function getImgUrl(array){
    6.                         const ret = [];
    7.                         const executing = [];
    8.                         return new Promise(async (resolve, reject) => {
    9.                             try {
    10.                                 for(let currentPage=0;currentPage<(+pageMax);currentPage++) {
    11.                                         let PageID = currentPage==0 ? `/${currentPageID}.html` : `/${currentPageID}_${currentPage}.html`,
    12.                                             PageUrl = location.pathname.replace(/\/[^/.]+\.html/i, PageID);

    13.                                     await $.get(PageUrl, (res,s,e)=>{
    14.                                         $('.content img', res).map(function(i, e){
    15.                                             ImgList.push(this.src);
    16.                                             //console.log(PageID, i, e, this.src)
    17.                                         });
    18.                                     })
    19.                                 }
    20.                             } catch(e){
    21.                                 console.error(e);
    22.                             } finally {
    23.                                 Promise.all(ret);
    24.                             }
    25.                         })
    26.                     }
    27.                     getImgUrl();


    28.         let padZero = (n) => (num) => {
    29.             if (typeof num === 'number') num += '';
    30.             return num.padStart(n, '0');
    31.         }


    32.     function requestUrlBlob(url) {
    33.         return new Promise((resolve, reject) => {
    34.             GM_xmlhttpRequest({
    35.                 url: url,
    36.                 method: 'get',
    37.                 responseType : 'blob',
    38.                 onload :  res => resolve(res.response),
    39.                 onerror : e => reject(e)
    40.             });
    41.         })
    42.     }

    43.     async function downloadAndPackFile(zipPack, url, filename, extname) {
    44.         if (!extname) {
    45.             const urlExt = url.split('.').pop();
    46.             extname = urlExt ? `.${urlExt}` : '';
    47.         }

    48.         console.log(`正在下载`, url, filename);
    49.         await requestUrlBlob(url).then((blob) => {
    50.             console.log(`下载完成`, url, blob, filename);
    51.             zipPack.file(`${filename}${extname}`, blob);
    52.         });
    53.     }

    54.     async function startDownload(ImgList, zipName) { // zipName = 压缩包名字
    55.         const zipPack = new window.JSZip();
    56.         const urlObjList = ImgList.map((url, idx) =>({url, filename: padZero(3)(idx),}));

    57.         console.log('开始下载');
    58.         await asyncPool(5, urlObjList, ({ url, filename }) => {
    59.             downloadAndPackFile(zipPack, url, filename);
    60.         });

    61.         await zipPack.generateAsync({ type: 'blob' }, function(metadata) {
    62.             let progress = metadata.percent / 100;
    63.             console.log(metadata, progress);
    64.         }).then(
    65.             function (blob) {
    66.                 // 1) generate the zip file
    67.                 window.saveAs(blob, `${zipName}.zip`); // 2) trigger the download
    68.             },
    69.             function (err) {
    70.                 console.error('[批量下载] ERR', err);
    71.             }
    72.         );
    73.         console.log('资源下载完成,开始打包资源', zipName);
    74.     }

    75.     async function asyncPool(poolLimit, array, iteratorFn) { //通过任务池限制并发数量,任务池 poolLimit=任务上限,array=任务对象,iteratorFn=任务内容
    76.         const ret = [];
    77.         const executing = [];
    78.         for (const item of array) {
    79.             const task = Promise.resolve().then((y, n) => {
    80.                 iteratorFn(item, array)
    81.             }); //等待处理完成
    82.             ret.push(task);

    83.             console.log(task);
    84.             if (poolLimit <= array.length) {
    85.                 const e = task.then(() => executing.splice(executing.indexOf(e), 1));
    86.                 executing.push(e);
    87.                 if (executing.length >= poolLimit) {
    88.                     await Promise.race(executing);
    89.                 }
    90.             }
    91.         }
    92.         return Promise.all(ret);
    93.     }
    94.         
    复制代码


    对 Promise 不太熟悉,如上面的地址

    startDownload 函数里

    asyncPool 负责下载图片和打包

    zipPack.generateAsync 执行下载动作

    但是 zipPack.generateAsync  没有等待 asyncPool 的 GM_xhr 完成就执行了下载动作,导致下载了一个空包
    该怎么调整代码执行?


    最佳答案

    查看完整内容

    感觉你对async/await的理解有问题,虽然强行异步后返回值变成了Promise,但代码实际还是同步的,这个downloadAndPackFile函数问题很大,return GM_xhr肯定得不到想要的结果,应该自己用Promise封装后return这个Promise(参考我给的download函数),asyncPool做的事跟我的downloadAll函数类似。
  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    881

    回帖

    1379

    积分

    荣誉开发者

    积分
    1379

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-1-24 07:22:37 | 显示全部楼层
    ThisAV 发表于 2022-1-24 15:59
    感谢大佬回帖,是我的描述有问题,现有的代码就是这个操作方法

    startDownload 函数里

    感觉你对async/await的理解有问题,虽然强行异步后返回值变成了Promise,但代码实际还是同步的,这个downloadAndPackFile函数问题很大,return GM_xhr肯定得不到想要的结果,应该自己用Promise封装后return这个Promise(参考我给的download函数),asyncPool做的事跟我的downloadAll函数类似。
    回复

    使用道具 举报

  • TA的每日心情

    2024-5-10 14:44
  • 签到天数: 23 天

    [LV.4]偶尔看看III

    4

    主题

    70

    回帖

    64

    积分

    初级工程师

    积分
    64

    油中3周年挑战者 lv1

    发表于 2022-1-24 07:32:20 | 显示全部楼层
    PS: 因为图片地址会进行 301 跳转,fetch 会受到 cors 影响
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-10-28 07:07
  • 签到天数: 193 天

    [LV.7]常住居民III

    712

    主题

    5961

    回帖

    6760

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6760

    荣誉开发者喜迎中秋油中2周年生态建设者

    发表于 2022-1-24 09:42:07 | 显示全部楼层
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-10-28 07:07
  • 签到天数: 193 天

    [LV.7]常住居民III

    712

    主题

    5961

    回帖

    6760

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6760

    荣誉开发者喜迎中秋油中2周年生态建设者

    发表于 2022-1-24 10:50:17 | 显示全部楼层
    看了一下,Gm_xhr不可以用await哦
    他没有返回promise
    可以自己封装一个
    https://bbs.tampermonkey.net.cn/thread-883-1-1.html
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    881

    回帖

    1379

    积分

    荣誉开发者

    积分
    1379

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-1-24 11:22:55 | 显示全部楼层
    给你写个简单示例:
    1. function download(url) {
    2.     return new Promise((resolve, reject) => {
    3.         GM_xmlhttpRequest({
    4.             url: url,
    5.             method: 'get',
    6.             responseType : 'blob',
    7.             onload: res => resolve(res.response),
    8.             onerror: e => reject(e)
    9.         });
    10.     });
    11. }
    12. function downloadAll(urlList) {
    13.     const task = [];
    14.     for (let url of urlList) {
    15.         task.push(download(url));
    16.     }
    17.     return Promise.all(task);
    18. }
    复制代码

    要等待全部下载完成,await downloadAll()即可。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-10-28 07:07
  • 签到天数: 193 天

    [LV.7]常住居民III

    712

    主题

    5961

    回帖

    6760

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6760

    荣誉开发者喜迎中秋油中2周年生态建设者

    发表于 2022-1-24 12:00:56 | 显示全部楼层
    cxxjackie 发表于 2022-1-24 11:22
    给你写个简单示例:

    要等待全部下载完成,await downloadAll()即可。

    大佬严谨!好久没看到cxxjackie哥哥了
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情

    2024-5-10 14:44
  • 签到天数: 23 天

    [LV.4]偶尔看看III

    4

    主题

    70

    回帖

    64

    积分

    初级工程师

    积分
    64

    油中3周年挑战者 lv1

    发表于 2022-1-24 14:58:27 | 显示全部楼层
    李恒道 发表于 2022-1-24 10:50
    看了一下,Gm_xhr不可以用await哦
    他没有返回promise
    可以自己封装一个

    意思是我只能用递归的方法去满足我的需求?
    回复

    使用道具 举报

  • TA的每日心情

    2024-5-10 14:44
  • 签到天数: 23 天

    [LV.4]偶尔看看III

    4

    主题

    70

    回帖

    64

    积分

    初级工程师

    积分
    64

    油中3周年挑战者 lv1

    发表于 2022-1-24 15:59:01 | 显示全部楼层
    cxxjackie 发表于 2022-1-24 11:22
    给你写个简单示例:

    要等待全部下载完成,await downloadAll()即可。

    感谢大佬回帖,是我的描述有问题,现有的代码就是这个操作方法

    startDownload 函数里

    asyncPool 负责下载图片和打包

    zipPack.generateAsync 执行下载动作

    但是 zipPack.generateAsync  没有等待 asyncPool 的 GM_xhr 完成就执行了

    该怎么优化这个?
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-10-28 07:07
  • 签到天数: 193 天

    [LV.7]常住居民III

    712

    主题

    5961

    回帖

    6760

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6760

    荣誉开发者喜迎中秋油中2周年生态建设者

    发表于 2022-1-24 17:18:53 | 显示全部楼层
    ThisAV 发表于 2022-1-24 14:58
    意思是我只能用递归的方法去满足我的需求?

    不用递归呀,
    直接包装GM_xhr返回一个promise
    然后全部塞到一个数组里
    promise.all函数等待全部返回成功
    进行下一步处理
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表