简体   繁体   English

使用 Promises 和 Fetch API javascript 获取多个文件

[英]Fetching multiple files using Promises and Fetch API javascript

I am updating my javascript skills with Promises, already have in place a library with XHR and callbacks to load and inject multiple files at once and only proceed if ALL of them succeeded.我正在使用 Promises 更新我的 javascript 技能,已经有一个带有 XHR 和回调的库,可以一次加载和注入多个文件,并且只有在所有文件都成功时才继续。

I am trying to use Promise.all() and Fetch API to get a similar functionality but can't make it work: console.log('All the promises are resolved', values);我正在尝试使用 Promise.all() 和 Fetch API 来获得类似的功能,但无法使其工作:console.log('All the promises areresolution', values); always triggers no matter if some of the fetch promises failed.无论某些 fetch 承诺是否失败,总是会触发。

I want to be able to execute the code below, and only proceed with nowInitialize function if all the files were able to be fetched, or throw error using catch() with the reason of the first file that failed我希望能够执行下面的代码,并且只有在所有文件都能够被获取时才继续使用 nowInitialize 函数,或者由于第一个文件失败的原因使用 catch() 抛出错误

xRequire(['index.html', 'style.cds'])
  .then(nowInitialize)
  .catch(reason => 'One or more files failed to load' + reason)

style.cds will obviously fail style.cds 显然会失败

//TODO Handle file types appropriately
//TODO: Inject css, javascript files

function xRequire(files) {
    let urls = [];
    let promisesList = [];
    let handleAllPromises;


//Populates urls with each file required
for(let i=0; i < files.length ; i++) {
    urls.push(files[i]);
}
//Fetch each file in urls
urls.forEach( (url, i) => { // (1)
    promisesList.push(
        fetch(url)
            .then(handleResponse)
            .then(data => console.log(data))
            .catch(error => console.log(error))
    );
});

handleAllPromises = Promise.all(promisesList);
handleAllPromises.then(function(values) {
    console.log('All the promises are resolved', values);
});
handleAllPromises.catch(function(reason) {
    console.log('One of the promises failed with the following reason', reason);
});
}

function handleResponse(response) {
let contentType = response.headers.get('content-type');
console.log('Requested Info: ' + contentType);
if (contentType.includes('application/json')) {
    return handleJSONResponse(response);
} else if (contentType.includes('text/html')) {
    return handleTextResponse(response);
} else if (contentType.includes('text/css')) {
    return handleTextResponse(response);
} else if (contentType.includes('application/javascript')) {
    return handleTextResponse(response);
} else {
    throw new Error(`Sorry, content-type ${contentType} not supported`);
}
}

function handleJSONResponse(response) {
return response.json()
    .then(json => {
        if (response.ok) {
            return json;
        } else {
            return Promise.reject(Object.assign({}, json, {
                status: response.status,
                statusText: response.statusText
            }));
        }
    });
}

function handleTextResponse(response) {
return response.text()
    .then(text => {
        if (response.ok) {
            return text;
        } else {
            return Promise.reject({
                status: response.status,
                statusText: response.statusText,
                err: text
            });
        }
    });
}

Can you just rewrite it as async-await code?您可以将其重写为异步等待代码吗? Here is a rough idea of the typical flow:以下是典型流程的粗略概念:

const [data1, data2, data3] = await Promise.all([
  fetch(url1),
  fetch(url2),
  fetch(url3),
]);

In other words, Promise.all() returns the promise to all the data that is returned from your multiple fetch() functions.换句话说, Promise.all()返回对从多个fetch()函数返回的所有数据的承诺。

Then, if you put this into a try-catch, you can handle the rejection as well:然后,如果你把它放到 try-catch 中,你也可以处理拒绝:

try {
  const [data1, data2, data3] = await Promise.all([
    fetch(url1),
    fetch(url2),
    fetch(url3),
  ]);

  // Now you can process the data:
  [data1, data2, data3].map(handleResponse);
} catch (error) {
  console.log('Error downloading one or more files:', error);
}

If you want to loop with async-await , you can do that:如果你想用async-await循环,你可以这样做:

const promises = [];
for (const url of [url1, url2, url3, url4]) {
  promises.push(fetch(url));
}

const [data1, data2, data3, data4] = await Promise.all(promises);

There are two problems.有两个问题。 First, you need to return the Promise.all call from xRequire in order to consume it in your xRequire(..).then :首先,您需要从xRequire返回Promise.all调用,以便在xRequire(..).then使用它:

return Promise.all(promisesList);

Also, when you use .catch , if a Promise is initially rejected, it will go into the catch block, do whatever code is there, and then the Promise chain will resolve (not reject) to whatever the catch block returns.此外,当您使用.catch ,如果Promise最初被拒绝,它将进入catch块,执行那里的任何代码,然后Promise链将解析(而不是拒绝) catch块返回的任何内容。 If you want to percolate errors up the Promise chain, put your catch at the point in the chain at which you want to detect errors:如果您想将错误向上渗透Promise链,请将您的catch放在链中您想要检测错误的位置:

urls.forEach( (url, i) => { // (1)
  promisesList.push(
    fetch(url)
    .then(handleResponse)
    .then(data => console.log(data))
    // no catch here
  );
});

I would suggest putting your catch only in the caller of xRequire , that way it will see all errors.我建议将您的catch放在xRequire的调用者中,这样就会看到所有错误。 Your xRequire function can be reduced to:您的xRequire函数可以简化为:

xRequire(['index.html', 'style.cds'])
  .then(nowInitialize)
  .catch(reason => 'One or more files failed to load' + reason)

function xRequire(files) {
  return Promise.all(
    urls.map(handleResponse)
  );
}

If you want the body of xRequire to be able to see errors, but you also want to percolate errors up the Promise chain, throw an error in a catch inside xRequire , so that the Promise it resolves to will reject , rather than resolve:如果您希望xRequire的主体能够看到错误,但您也希望将错误渗透到Promise链中,请在xRequire内的catch抛出错误,以便它解析为的Promise拒绝,而不是解析:

function xRequire(files) {
  return Promise.all(
    urls.map(handleResponse)
  )
  .catch((err) => {
    console.log('There was an error: ' + err);
    throw err;
  })
}

I finally solved it in this way --with the only quirk i've found so far: files argument always needs to be an array , therefore always needs brackets when calling the function--我终于以这种方式解决了它——到目前为止我发现的唯一怪癖:文件参数总是需要是一个数组,因此在调用函数时总是需要括号——

xRequire(['my-file'])
   .then(handle success)
   .catch(handle error);

async function xRequire(files) {
    let promises = [];
    let receivedData;

    //Iterate over files array and push results of fetch to promises array
    files.map(x => promises.push(fetch(x)));
    //populate receivedData array from promises array
    receivedData = await Promise.all(promises);
    //iterate over receivedData to handle each response accordingly
    return receivedData.map(x => handleResponse(x));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM