【问题标题】:Fetching multiple files using Promises and Fetch API javascript使用 Promises 和 Fetch API javascript 获取多个文件
【发布时间】:2019-03-27 04:56:20
【问题描述】:

我正在使用 Promises 更新我的 javascript 技能,已经有一个带有 XHR 和回调的库,可以一次加载和注入多个文件,并且只有在所有文件都成功时才继续。

我正在尝试使用 Promise.all() 和 Fetch API 来获得类似的功能,但无法使其工作:console.log('All the promises are resolved', values);无论某些 fetch 承诺是否失败,都会触发。

我希望能够执行下面的代码,并且只有在所有文件都能够被获取的情况下才继续使用 nowInitialize 函数,或者使用 catch() 抛出错误,原因是第一个文件失败

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

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
            });
        }
    });
}

【问题讨论】:

  • @CertainPerformance 你对这不是重复的事情并不认真吗?我的意思是一个简单的搜索显示 SO 上的多个配音......来吧。这是一个:stackoverflow.com/questions/31710768/…
  • @Akrion 我没有这么说,我只是说async/await 语法问题不是问题所要问的(问题中至少有几个问题)

标签: javascript ecmascript-6


【解决方案1】:

我终于以这种方式解决了它——到目前为止我发现的唯一怪癖是:files 参数总是需要是一个 array,因此总是需要括号调用函数时--

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));
}

【讨论】:

    【解决方案2】:

    你能把它重写为 async-await 代码吗?以下是典型流程的粗略概念:

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

    换句话说,Promise.all() 将承诺返回给从多个 fetch() 函数返回的所有数据。

    然后,如果您将其放入 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);
    }
    

    如果你想循环使用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);
    

    【讨论】:

    • 这看起来不错,但是当循环访问所有被请求的文件时,我无法使语法正常工作,例如 xRequire(['url1', 'url2', 'url3', 'url4'] ),你能详细说明一下吗?
    • @aalvarado 添加
    【解决方案3】:

    有两个问题。首先,您需要返回来自xRequirePromise.all 调用,以便在您的xRequire(..).then 中使用它:

    return Promise.all(promisesList);
    

    另外,当你使用.catch 时,如果Promise 最初被拒绝,它将进入catch 块,执行那里的任何代码,然后Promise 链将解决(不拒绝)catch 块返回的任何内容。如果您想在 Promise 链上渗透错误,请将您的 catch 放在链中您要检测错误的位置:

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

    我建议将您的catch 只放在xRequire 的调用者 中,这样it 会看到所有错误。您的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)
      );
    }
    

    如果您希望xRequire 的主体能够看到错误,但您还希望将错误向上渗透到Promise 链,请在xRequire 内的catch 中抛出错误,以便@ 987654341@ 它解析为将reject,而不是解析:

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

    【讨论】:

    • 在您的简化功能中,您能指出您实际从服务器获取文件的位置吗?
    猜你喜欢
    • 1970-01-01
    • 2018-10-28
    • 2018-08-23
    • 2018-09-29
    • 2022-01-20
    • 2020-02-12
    • 2016-12-13
    • 1970-01-01
    • 2020-08-07
    相关资源
    最近更新 更多