async/await与for循环遍历
发布于 6 年前 作者 ZCZ12138 9739 次浏览 来自 问答

遇到了在for循环中,使用await函数的情况,但是打断点调试后发现得到的还是Promise{pending},有考虑闭包的问题,使用了匿名函数带参数的写法,还是不能解决,每次单步调试的时候await函数那里会自动跳过,代码如下,请熟悉这方面的朋友给点意见,指明一个研究方向,谢谢。

var query = models.DuoBaoPlayer.find(q, '').sort('-_id').skip(skipFrom).limit(limit);

query.exec(function(err, doc) {
    var dataList = [];
    
	async function findOneDuobao(item) {
        let doc = await models.DuoBao.findOne({id:item.sid}, 'title startTime endTime luckys totalTime haveTime winner');		//mongoose
        return doc;
    }
        
    for (let i = 0; i < doc.length; i++) {
        dataList.push((async function (num) {
            let data1 = doc[num].toObject();			
            let data2 = await findOneDuobao(doc[num]);              //每次单步调试的时候都会自动跳过
            let data = Object.assign(data1, data2);
            return data;
        })(i));
    }
    
    let testDataList = dataList;                //return 两个 Promise { pending }
}
20 回复

image.png image.png

打开下面的网页自己试一下。

http://www.typescriptlang.org/play/index.html#src= let arr %3D [] for (let i %3D 0%3B i < 2%3B i%2B%2B){ const ret %3D ((async (i) %3D> { return Promise.resolve(i) })(i))%3B%0D%0A%20%20%20%20arr.push(ret)%0D%0A%7D%0D%0A%0D%0Aconsole.log(arr)%0D%0A%0D%0A

没问题的,自己 catch 一下。

let testDataList = await Promise.all(dataList);

async 函数返回的就是一个 Promise,你的 datalist push 进去的当然也是个 pending 状态的 Promise,所以里面的 await 调试取值的时候也是一个 pending 状态的 Promise,当然也就看起来跳过了

先不说别的,你上面代码中是不是漏了什么,doc 没有声明啊,或者忘执行函数了

@rrbe 我想得到的是resolve之后的值,有什么办法能够让它完成后再执行呢

@ZCZ12138 当然参考是 2 楼朴灵大大的回复~ 哈哈 当然你要把外层也用 async 函数包起来

  let dataList = [];

  function findOneDuobao(item) {
    return new Promise((resolve) => {
      setTimeout(function () {
        resolve(item)
      },Math.random()*1000);
    });
  }

  (async function() {
    for (let i=0;i<10;i++) {
      let result=await findOneDuobao(i)
      console.log(result)
      dataList.push(result)
    }
    console.log(dataList)
  })()

@Michaelooo function(err, doc) { }

@ZCZ12138 眼瞎没看到……其他的感觉没毛病啊,我测的OK啊

async function findone() {
    return { 'aa': '11'};
}

async function findOneDuobao (a) {
    let ret = await findone();
    return ret;
}

var dataList = [];
for (let i = 0; i < 2; i++) {
  dataList.push((async function (num) {
    let data1 = {'data' : '22'};
    let data2 = await findOneDuobao({ 'data' : '33'});              //每次单步调试的时候都会自动跳过
    let data = Object.assign(data1, data2);
    return data;
  })(i));
}

console.log(dataList)

@JacksonTian 您的建议确实让人耳目一新,但我测试后发现all之后的值少了一个字段,不太确定是不是这样写的原因,我改用async.eachSeries是可以获取到完整的数据的,而且请求在Chrome里面没有response data,这是为什么?

async (xxx) => { const query = await model.xx.find(); if(query && query.length) { for(let i in query){ let data = await model.xxx.find(i) } } }

我平时就是这样用的,不知道是否符合你的要求

有始有终一下,Promise.all确实有效,少字段是因为assign函数的问题,没有response data是因为浏览器,感谢大家的建议。另外有关于遍历中的异步操作,大家有什么更好的建议请不要吝啬,随意砸到我脸上 》_《

我觉得大家没有引导楼主到正确的姿势上额,楼主对async 和 await 还缺一点点更深刻的理解,这里给你个引导吧:

const exec = models.DuoBaoPlayer.find(q, '').sort('-_id').skip(skipFrom).limit(limit).exec;

async function findOneDuobao(item) {
        let doc = await models.DuoBao.findOne({id:item.sid}, 'title startTime endTime luckys totalTime haveTime winner');		//mongoose
        return doc;
}

exec(function(err, doc) {
    (async (err,  doc)=>{
	  var dataList = [];
	   
	   //写法1,一个一个执行
	  for (let i = 0; i < doc.length; i++) {
	       let data1 = doc[i].toObject();	
           dataList.push(await findOneDuobao(doc[i]));   		   
	  }		
	  
	  //写法2,用Promise.all
	 const tasks = [];
	 for (let i = 0; i < doc.length; i++) {
	       tasks.push(findOneDuobao(doc[i]));
	  }	
	  dataList = Promise.all(tasks);
	  
	}))(err, doc)
}

//或者在封装一下写法如下:
const wrapFn = asyncFn => (err, doc) => asyncFn(err, doc); 
async function findDataList(err,  doc){
	  var dataList = [];
	  for (let i = 0; i < doc.length; i++) {//这里如上,写法1和写法2都ok
	       let data1 = doc[i].toObject();	
           dataList.push(await findOneDuobao(doc[i]));   		   
	  }	
	  return dataList;	  
}
exec(wrapFn(findDataList));

//或者再简单粗暴点
const util = require('util');
const exec = models.DuoBaoPlayer.find(q, '').sort('-_id').skip(skipFrom).limit(limit).exec
const execPromise = util.promisify(exec);

const findDataList = async function(){
	const docs = await execPromise();
	const dataList = [];
	const len = docs.length;
    for (let i = 0; i < len; i++) {
           dataList.push(await findOneDuobao(docs[i]));   		   
	}
	return dataList
}

findDataList().then(dataList=> console.log(dataList)).catch(err=> console.log(err))

@fantasticsoul await就是在async函数的promise变为resolved后,相当于.then((value) => {}),而不加await关键字只是个待完成的promise而已,而我题目中明明已经await了,但还是不能等到了resolved后才执行下文,这让我很困惑,或许还是陷入怪圈出不来了 (ノへ ̄、)

@qiu363 因为 async 返回的是一个 promise

@ZCZ12138 谷歌浏览器console里执行如下代码,没问题啊

for(var i=1;i<5;i++){
	(async(i)=>{
        console.log(i);
        var resp = await fetch('http://www.zzkai.com:8888/getbooks');
        var json = await resp.json();
        console.log(i,json);
	})(i)
}

你试一下push()里面的匿名函数执行完后给个.then(result => result) ,这样push((async fun)(i).then(result => result)), 不知道行不行,如果可以 那么 在前面 加个push( await (async funtion(){})(i)) 应该也可以

试了一下 发现 加.then()那个不可以的,await匿名函数前面加个 await是可以的,第一个不行的原因不太清楚,报的错是[ Promise { [Circular] }, Promise { [Circular] } ],

回到顶部