Javascript 对承诺和异步等待感到困惑

Javascript 对承诺和异步等待感到困惑,javascript,async-await,es6-promise,Javascript,Async Await,Es6 Promise,我正在使用GitHub api创建应用程序,但异步函数遇到了问题。我不熟悉使用异步,因此非常感谢您的帮助。以下是我迄今为止编写的代码: const getFiles = async function(token, reponame) { var gh = new GitHub({ token: token }); reponame = reponame.split("/"); const repo = gh.getRepo(reponame[0], reponame[1

我正在使用GitHub api创建应用程序,但异步函数遇到了问题。我不熟悉使用异步,因此非常感谢您的帮助。以下是我迄今为止编写的代码:

const getFiles = async function(token, reponame) {
  var gh = new GitHub({
    token: token
  });

  reponame = reponame.split("/");
  const repo = gh.getRepo(reponame[0], reponame[1]);

  let head = new Headers();
  head.append("Authorization: ", "token " + token);

  const getContents = new Promise((res, rej) => {
    repo.getContents(null, "content", true, (err, files) => {
      if (err) rej(err);
      else return files;
    }).then(files => {
      let promises = [
        files.map(
          file =>
            new Promise(res => {
              fetch(file.downloadURL).then(body => {
                res(body.text);
              });
            })
        )
      ];

      const retFiles = [];
      await Promise.all(promises.map(promise => retFiles.push(promise)));
      res(retFiles)
    });
  });

  return getContents;
};

我得到的错误是在我使用wait的行中出现了意外的保留字。提前感谢

所以我在这里做一些假设,如果我错了,请纠正我,我会修正它们。希望通过这样做,我可以帮助澄清您的理解

思考
async/await
的一种简单方法是取代对
.then(回调)
的需求。我更喜欢在
异步函数中使用
wait

const getFiles = async function(token, reponame) {
  try {
    var gh = new GitHub({
      token: token
    });
    reponame = reponame.split("/");

    // edit: I removed await from the line below as your original code
    //       did not treat it as a promise
    const repo = gh.getRepo(reponame[0], reponame[1]); 

    // unused code from your post
    let head = new Headers();
    head.append("Authorization: ", "token " + token);

    // the await below assumes that repo.getContents 
    // will return a promise if a callback is not provided
    const files = await repo.getContents(null, "content", true); 

    // updating the code below so that the file requests run in parallel.
    // this means that all requests are going to fire off basically at once
    // as each fetch is called
    const fileRequests = files.map(file => fetch(file.downloadURL))

    // you wont know which failed with the below.
    const results = (await Promise.all(fileRequests)).map(res => res.text)
    return results
  } catch (err) {
    // handle error or..
    throw err;
  }
};
此代码未经测试。我没有使用github的api,所以我最好猜测每个调用都在做什么。如果
gh.getRepo
repo.getContents
不返回承诺,则需要进行一些调整

如果未提供回调,您正在使用的github库将不会返回承诺:

const getFiles = async function(token, reponame) {
  try {
    var gh = new GitHub({
      token: token
    });
    reponame = reponame.split("/");
    const repo = await gh.getRepo(reponame[0], reponame[1]); 
    let head = new Headers();
    head.append("Authorization: ", "token " + token);

    const getContents = new Promise((res, rej) => {
      repo.getContents(null, "content", true, (err, files) => {
        if (err) { 
          return rej(err);
        }
        res(files)
      })
    })
    const fileRequests = (await getContents).map(file => fetch(file.downloadURL))
    return (await Promise.all(fileRequests)).map(res => res.text)
  } catch (err) {
    // handle error or..
    throw err;
  }
};
下面是一个使用async/await的示例,它使用一个新的承诺来承诺回调:

const content=document.getElementById(“内容”)
const result=document.getElementById(“结果”)
异步函数示例(){
content.innerHTML='承诺之前';
const getContents=新承诺((res,rej)=>{
设置超时(()=>{
res(‘完成’)
}, 1000)
})
const res=等待获取内容
content.innerHTML=res
返回res
}
示例()。然后((res)=>{
result.innerHTML=`I以${res}作为结果完成`
})

我发现这是修订后的代码:

const getFiles=async函数(令牌、reponame){
var gh=新的GitHub({
令牌:令牌
});
reponame=reponame.split(“/”);
常量repo=gh.getRepo(reponame[0],reponame[1]);
让文件=新承诺((res,rej)=>{
repo.getContents(null,“content”,true,(err,files)=>{
if(err)rej(err);
其他资源(档案);
});
});
让内容=新承诺(res=>{
文件。然后(文件=>{
const promises=files.reduce((结果,文件)=>{
if(file.name.endsWith(“.md”)){
结果:推(
新承诺((res,rej)=>{
repo.getContents(null,file.path,true,(err,content)=>{
if(err)rej(err);
其他的
res({
path:file.path,
内容:内容
});
});
})
);
}
返回结果;
}, []);
console.log(承诺);
res(
我保证(
promises.map(promise=>
然后(文件=>{
返回文件;
})
)
)
);
});
});
返回等待内容;
};

我仍然不知道这是否是一种“正确”的方法,但它正在工作。

关键字
wait
只能与
异步
功能一起使用。如果您注意到,
等待Promise.all(promises.map(Promise=>retFiles.push(Promise))
位于一个函数内,该函数在
中传递
文件
参数,然后
。只需使该函数
async
wait
在范围内工作即可。请尝试下面的代码

 const getFiles = async function(token, reponame) {
  var gh = new GitHub({
    token: token
  });

  reponame = reponame.split("/");
  const repo = gh.getRepo(reponame[0], reponame[1]);

  let head = new Headers();
  head.append("Authorization: ", "token " + token);

  const getContents = new Promise((res, rej) => {
    repo.getContents(null, "content", true, (err, files) => {
      if (err) rej(err);
      else return files;
    }).then( async (files) => {
      let promises = [
        files.map(
          file =>
            new Promise(res => {
              fetch(file.downloadURL).then(body => {
                res(body.text);
              });
            })
        )
      ];

      const retFiles = [];
      await Promise.all(promises.map(promise => retFiles.push(promise)));
      res(retFiles)
    });
  });

  return getContents;
};

这里有很多问题。代码非常复杂;不需要所有这些承诺和间接层和嵌套来实现您所需要的

您尝试执行的模式非常常见:

  • 请求获取实体列表(文件、用户、URL…)
  • 对于该列表中返回的每个实体,发出另一个请求以获取其更多信息
  • 将结果作为承诺返回(它必须是承诺,因为
    async
    函数只能返回承诺)
这样做的方法是将问题分为几个阶段。使用
wait
async
关键字代替
。然后在大多数情况下使用
。为了使示例重现,我将使用一个场景,在这个场景中,我们希望获得在GitHub上创建的最新的
n
GIST的用户配置文件——这基本上等同于您正在做的事情,我将留给您进行推断

第一步是获取实体(最近创建的GIST)的初始列表:

接下来,对于来自
0..n
的gist数组中的每个gist,我们需要发出一个请求。确保我们没有使用
wait
序列化此处的任何内容,这一点很重要:

const requests = gists.slice(0, n).map(gist =>
  fetch(`https://api.github.com/users/${gist.owner.login}`)
);
现在所有的请求都在进行中,我们需要等待它们完成。这就是
承诺的地方。所有
都包括:

const responses = await Promise.all(requests);
最后一步是从每个响应中获取JSON,这需要另一个
承诺

return await Promise.all(responses.map(e => e.json()));
这是我们可以返回的最终结果。代码如下:

const getRecentGistCreators=async(n=1)=>{
试一试{
const res=等待获取(“https://api.github.com/gists/public");
const gists=wait res.json();
const requests=gists.slice(0,n).map(gist=>
取回(`https://api.github.com/users/${gist.owner.login}`)
);
const responses=等待承诺。全部(请求);
return wait Promise.all(responses.map(e=>e.json());
}
捕捉(错误){
犯错误;
}
};
(异步()=>{
试一试{
for(Wait getRecentGistCreators的常量用户(5)){
常量elem=document.createElement(“div”);
elem.textContent=user.name;
文件.正文.附件(elem);
}
}
捕捉(错误){
犯错误;
}

})();欢迎来到SO!不能使用
wait
,除非它直接位于带有
async
关键字的函数内部。将
files=>{
更改为
async files=>{
。另外,将
let promises
设置为2d数组有点奇怪--只需将
map
的结果直接分配给它,否则您就有一个包含promises数组的单元素数组。除了这个数组之外,您的代码还有很多问题
return await Promise.all(responses.map(e => e.json()));