
hello 大家好,
我之前一直用 python 写一些小工具,最近开始用 JS 写东西,发现各种不适应:要么忘记放 ; , 要么数不清 { 和 } 是否成对。 这些都还好,多写一写也就习惯了,现在碰到一个代码执行顺序的逻辑问题:我有一个组订单号码,每个订单号码都要拿去进行 GET 请求,请求结果有一个变量要么 true 要么 false,我需要将根据这个变量将原始的订单号码分两组。
假设订单号码列表为:ordersID = [11, 12, 13, 21]
ordersID = [11, 12, 13, 21]; successful = list(); fail = list(); for x in ordersID: if (...): successful.append(x) else: fail.append(x) print(successful, fail) # [11,12, 13] [21] 为了精简我把条件部分省掉了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" cOntent="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>Testing!</h1> <script> var ordersID = ['11', '12', '13', '21']; var successful = []; var fail = []; function makeRequest(arg) { fetch(`https://jsonplaceholder.typicode.com/posts/${arg}`, {method: 'GET'}) .then(res => res.json()) .then(function (res) { if (res.userId == 2) { console.log(res); successful.push(arg); fetch('https://jsonplaceholder.typicode.com/posts', { method: 'POST', body: JSON.stringify({ title: 'Some title', body: arg, userId: 2 }), headers: { 'Content-type': 'application/json; charset=' } }) .then(res => res.json()) .then(console.log) } else { console.log('userId != 2'); fail.push(arg) } }); }; for (i = 0; i < ordersID.length; i++) {makeRequest(ordersID[i]); console.log(successful, fail)}; </script> </body> </html> 我期望的结果是返回一个successful array 和一个 fail array,分别包含成功和失败的订单号码。可结果是返回空的 array 。我 JS 还没有好好学,只是边做边查,哪位盘友指点一下 :)
1 lonelinsky 2020-05-27 13:38:21 +08:00 (你要不先研究下 Python3 的 asyncio,等研究完回来再看,可能就知道问题所在了 :-) |
2 jybox 2020-05-27 13:39:16 +08:00 Promise.all 或 Promise.allSettled |
3 Marven 2020-05-27 13:43:15 +08:00 用 async await |
4 rabbbit 2020-05-27 13:44:26 +08:00 fetch(`https://jsonplaceholder.typicode.com/posts/${arg}`, {method: 'GET'}) -> return fetch(`https://jsonplaceholder.typicode.com/posts/${arg}`, { method: 'GET' }) for (i = 0; i < ordersID.length; i++) {makeRequest(ordersID[i]); console.log(successful, fail)}; -> for (i = 0; i < ordersID.length; i++) { makeRequest(ordersID[i]).then(() => { console.log(successful, fail); }); }; 相关知识点 Promise event loop macrotask 和 microtask |
5 sudoy OP |
6 azcvcza 2020-05-27 14:50:42 +08:00 你的 console.log 最好都丢在和 then 同一个括号里; 首先 JS 自顶向下执行代码, 如果碰到 setTimeout,或者 promise 就把他塞到事件队列尾,下一个周期再执行;你这里 console.log,必然拿不到请求的数据 |
7 CyHstyle 2020-05-27 14:59:51 +08:00 浏览器执行 js 是有顺序的,自顶向下执行所有的同步 js,然后再执行异步回调。所以你先用一个 for 循环去调用 fetch,然后就 console 了结果,那么此时 fetch 还没有请求回来,是一个异步的,所以 console 打印出来的就是空的,要在 fetch 的回调函数里打印,或者,用 async await 阻塞异步请求。 |
8 sayitagain 2020-05-27 15:01:23 +08:00 js 请求并不会等到请求结束才往下走,有可能这次请求发出去还没收到响应值就已经执行 console.log(successful, fail)了...要么把 console.log(successful, fail)放在 then 里,要么把请求设置为同步... |
9 whatCanIDoForYou 2020-05-27 15:31:36 +08:00 |
10 JayLin1011 2020-05-27 15:49:28 +08:00 `Promise.allSettled()` 能满足你的需求啊,请求结果自带 `status`,根据它判断成功失败就好,或者直接根据有没有 `value` 或 `reason` 判断也行。你这种属于串行的继发请求模式,`async..await` 语法会更适合这个场景,写起来时序更直观,不然像这样容易就把 `Promise` 优雅的链式调用活生生写成「回调套娃」。温馨提示:建议 `fetch()` 可以封装一下。 https://developer.mozilla.org/zh-CN/docs/Web/Javascript/Reference/Operators/await |
11 Shy07 2020-05-27 16:15:56 +08:00 JS 一般推荐函数式的写法: const promises = orderID.map(makeRequest) const result = await Promise.all(promises) const successfull = result.filter(val => res.status === 'ok') const fail = result.filter(val => res.status !== 'ok') |
12 JayLin1011 2020-05-27 16:27:53 +08:00 @Shy07 `status` 的值要么 `fulfilled`,要么 `rejected`。。 |
13 Shy07 2020-05-27 16:39:00 +08:00 @JayLin1011 个人比较倾向在 makeRequest 里预先处理一下请求结果,比如返回 { status: string, arg: any, err: Error } |
14 sudoy OP |
15 JayLin1011 2020-05-27 17:10:57 +08:00 @Shy07 。。不是请求处理的问题,`Promise.all()` 的返回值格式是固定的。 |
16 jones2000 2020-05-27 18:57:22 +08:00 直接 js 裸写一个下载队列,批量下载不就可以了。如果迁移到其他平台,换成多线程下载就可以了。 ```Javascript function OnFinished(aryResult) { console.log("下载完成了", aryResult) } var download=new JSDownload(); download.FinishCallback=OnFinished; var downloadList= [ {Url:"https://abc"}, {Url:"https://abc1"}, {Url:"https://abc2"} ]; download.SetDownload(downloadList); download.Start(); ``` ```Javascript //批量下载 function JSDownload() { this.DownloadData; /* Key:url Value: { Status: 状态 0=空闲 1=下载中, 20=下载成功 30=失败 , Message:错误信息, ProcSuccess: 单个数据到达回调(不处理就 null) ProcFailed: 单个数据失败回调(不处理就 null) RecvData: 接收到的数据 } */ this.FinishCallback; //全部下载完回调 //设置批量下载地址 this.SetDownload=function(aryDownload) { this.DownloadData=new Map(); for(var i in aryDownload) { var item=aryDownload[i]; if (this.DownloadData.has(item.Url)) continue; var downItem={Url:item.Url, ProcSuccess:item.ProcSuccess, ProcFailed:item.ProcFailed , Status:0 }; this.DownloadData.set(downItem.Url,downItem); } } //是否全部下载完成 this.OnFinished=function() { for (var item of this.DownloadData) //遍历下载队列 是否都下载完了 { if (item[1].Status==20 || item[1].Status==30) continue; return; } if (this.FinishCallback) //通知回调 数据都下载完了 { var aryResult=[]; for (var item of this.DownloadData) { var downloadItem=item[1]; if (downloadItem.Status==20) aryResult.push({Url:downloadItem.Url, Success:true, Data:downloadItem.RecvData}); else aryResult.push({Url:downloadItem.Url,Success:false}); } this.FinishCallback(aryResult); } } //开始下载 this.Start=function() { var self=this; for (var item of this.DownloadData) { console.log('[JSDownload::Start] start dowloand ', item[0]); this.AjaxDownload(item[1]); } } this.AjaxDownload=function(item) { var self=this; $.ajax({ url: item.Url, type:"get", dataType: "json", async:true, success: function (data) { if (item.ProcSuccess) item.ProcSuccess(data, self); item.RecvData=data; item.Status = 20; self.OnFinished(); }, error:function(jqXHR, textStatus, errorThrown) { if (item.ProcFailed) item.ProcFailed(jqXHR,self); item.Status = 30; self.OnFinished(); } }); item.Status=1; } } ``` |
17 Shy07 2020-05-27 20:52:01 +08:00 @JayLin1011 `Promise.all()` 返回值是函数的返回值 https://developer.mozilla.org/zh-CN/docs/Web/Javascript/Reference/Global_Objects/Promise/all `Promise.allSettled()` 才是固定的 https://developer.mozilla.org/zh-CN/docs/Web/Javascript/Reference/Global_Objects/Promise/allSettled |
18 JayLin1011 2020-05-27 21:46:37 +08:00 @Shy07 对。但我的 `promises` 不会全部成功,我还是选择 `Promise.allSettled()`。 |
19 oukichi 2020-05-27 21:54:05 +08:00 为啥要 then 里面套 then ? |
20 linZ 2020-05-28 11:13:44 +08:00 可以 resolve(promise)的,然后试一下 async await 呀,看下谁给你写个 async await 版本的,你就明白啦 |
21 pomelotea2009 2020-05-28 12:28:40 +08:00 via Android 搜:js async all |