简体   繁体   English

如何在 Promise.all 中的第二个中使用第一个 promise 的结果,依此类推

[英]How to use the result of first promise in the second and so on in Promise.all

By following a good answer by TJ Crowder to a SO Thread, I managed to combine a loop of async tasks with Promise.all.通过遵循TJ Crowder对 SO Thread 的一个很好的回答,我设法将异步任务循环与 Promise.all 结合起来。 The actual problem is, first I want to read one excel file in a Promisified function and a list of image files in the second Promisified function.实际问题是,首先我想在 Promisified 函数中读取一个 excel 文件,并在第二个 Promisified 函数中读取图像文件列表。 Here is the code functions performing files reading.这是执行文件读取的代码函数。

import { User } from "./types";
import * as XLSX from "xlsx";


// Loading users data from Excel Data... Id,Name,CardNo
export async function loadUsersData(usersFile: File) {
  let result_users: User[] =await new Promise((resolve) => {
    var reader = new FileReader();
    reader.onload = function (e) {
      const data = e.target.result;
      const readedData = XLSX.read(data, { type: 'binary' });
      const wsname = readedData.SheetNames[0];
      const ws = readedData.Sheets[wsname];

      /* Convert array to json*/
      const parsedData = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false });
      parsedData.shift();
      const users: User[] = parsedData.map((item: any) => {
        const id = item[0].toString().trim();
        const name = item[1].toString().trim();
        const cardNo = item[2].toString().trim();
        const user: User = { id, name, cardNo }; 
        return user;
      });

      resolve(users);
    }
    reader.readAsBinaryString(usersFile)

  });

  return result_users;
}


//Loading Images of Users Faces to display in material table along with other user info
export async function loadUsersFaces(users: User[], facesList: FileList) {
  const facesArray = Array.from(facesList)
  const promises=facesArray.map(async face=>{
    return await readFace(face, users);
  })
  let result_users: any=await Promise.all(promises);
  return result_users
}

function readFace(face: File,users:User[]) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = function (e) {
      let faceBase64String = e.target.result; //getting Base64String of image to render as custom column in material-table as https://material-table.com/#/docs/features/custom-column-rendering
      users.map(user => {
        if (face.name.includes(user.id) && face.name.includes(user.name)) {
          let newUser={ ...user, face: faceBase64String };
          console.log(`Resoling ${JSON.stringify(newUser)}`);
          resolve(newUser);
        }
      })
    }
    reader.readAsDataURL(face)
  });
}

And here is the code of Actions performing files reading one after the other.这是执行一个接一个读取文件的操作的代码。

//Here is usersFile is an excel file Blob and FileList contain list of image files
export const loadUsers = (usersFile: File,faces: FileList) => (dispatch:Dispatch) => {
  dispatch(actions.startCall({ callType: callTypes.list }));
  usersService.loadUsersData(usersFile).then((users:any)=>{  // Don't know how to tell compiler that it's User[]
    usersService.loadUsersFaces(users,faces).then((users:any)=>{
      console.log(users); // Here I should have users including Base64 Strings of face images in face property
      dispatch(actions.usersFetched({ totalCount:users.length, entities:users }));
    })
  })
};

If you want to use loops to chain promises you will need async & await如果您想使用循环来链接承诺,您将需要async & await

async function chainPromiseNTimes(function_returning_promise,n,data){

  
    for(let i=0;i<n;i++) {

       data = await function_returning_promise(data)
       // data (modified) will be passed to new promise in next iteration
    }

    return data;
}

let result = await chainPromiseNTimes(doSomeAsyncStuff, 5, arrayofObjs)

You could try pushing functions returning promises instead of promises onto your array.您可以尝试将返回承诺而不是承诺的函数推送到您的数组上。 This way you could simply call them when the data from the last promise is actually available.这样你就可以在最后一个承诺的数据实际可用时简单地调用它们。

 function doSomeAsyncStuff(arrayofObjs) { // this is not rly asynchronous but for the purpose of example will do return new Promise(function(resolve) { const result = arrayofObjs.map(obj => ++obj); resolve(result); }); } async function waitForPromiseChain(initialData, functionCallbacks) { let temp = initialData; for (let i = 0, l = functionCallbacks.length; i < l; i++) temp = await functionCallbacks[i](temp); return temp; } const promises = []; for (i = 0; i < 5; i++) { promises.push((arrayofObjs) => doSomeAsyncStuff(arrayofObjs)); } waitForPromiseChain([0, 0, 0, 0, 0], promises) .then(console.log);

In the example above I tried to keep code as close to your original as possible.在上面的例子中,我试图让代码尽可能接近你的原始代码。 However i took the liberty of redesigning function callbacks to accept any function in a chain instead of a single one.然而,我冒昧地重新设计函数回调以接受链中的任何函数而不是单个函数。

If you are opposed to using async/await the same effect can be achieved with usage of normal then , even if with some difficulty.如果您反对使用async/await ,即使有一些困难,也可以使用 normal then来实现相同的效果。

 function doSomeAsyncStuff(arrayofObjs) { // this is not rly asynchronous but for the purpose of example will do return new Promise(function(resolve) { const result = arrayofObjs.map(obj => ++obj); resolve(result); }); } function waitForPromiseChain(initialData, functionCallbacks) { let temp = Promise.resolve(initialData); for (let i = 0, l = functionCallbacks.length; i < l; i++) temp = temp.then(data => functionCallbacks[i](data)); return temp; } const promises = []; for (i = 0; i < 5; i++) { promises.push((arrayofObjs) => doSomeAsyncStuff(arrayofObjs)); } waitForPromiseChain([0, 0, 0, 0, 0], promises) .then(console.log);

My answer to this other question comes close to answering this, but I'm not sure it completely does.我对另一个问题的回答接近于回答这个问题,但我不确定它是否完全正确。

Since you want to use the first operation's result in the second, and the second operation's result in the third, etc., you can't run the asynchronous actions in parallel.由于您想在第二个操作中使用第一个操作的结果,在第三个中使用第二个操作的结果,依此类推,您不能并行运行异步操作。 So you have to run them in series.所以你必须串联运行它们。

If you can use an async function (well supported these days), you'd do that like something this:如果您可以使用async函数(这些天得到很好的支持),您可以这样做:

async function doSeriesOfThings() {
    let lastResult = /* the first value to pass, perhaps `undefined` or `null` */;
    for (const obj of arrayofObjs) {
        lastResult = await doSomeAsyncStuff(obj, lastResult);
    }

    return lastResult;
}

Live Example:现场示例:

 const arrayofObjs = [ {value: 1}, {value: 2}, {value: 3}, ]; function doSomeAsyncStuff(obj, value) { console.log(`doSomeAsyncStuff(${JSON.stringify(obj)}, ${value})`); return new Promise(resolve => { setTimeout(() => { resolve(obj.value + value); }, Math.random() * 500); }); } async function doSeriesOfThings() { let lastResult = 0; for (const obj of arrayofObjs) { lastResult = await doSomeAsyncStuff(obj, lastResult); } return lastResult; } doSeriesOfThings() .then(result => console.log(`Final result: ${result}`)) .catch(error => console.error(`Error: ${error.message || String(error)}`));

If you also need an array of results, just build it up in the function:如果您还需要一组结果,只需在函数中构建它:

async function doSeriesOfThings() {
    const results = [];
    let lastResult = /* the first value to pass, perhaps `undefined` or `null` */;
    for (const obj of arrayofObjs) {
        lastResult = await doSomeAsyncStuff(obj, lastResult)
        results.push(lastResult);
    }

    return results;
}

Live Example:现场示例:

 const arrayofObjs = [ {value: 1}, {value: 2}, {value: 3}, ]; function doSomeAsyncStuff(obj, value) { console.log(`doSomeAsyncStuff(${JSON.stringify(obj)}, ${value})`); return new Promise(resolve => { setTimeout(() => { resolve(obj.value + value); }, Math.random() * 500); }); } async function doSeriesOfThings() { const results = []; let lastResult = 0; for (const obj of arrayofObjs) { lastResult = await doSomeAsyncStuff(obj, lastResult) results.push(lastResult); } return results; } doSeriesOfThings() .then(result => console.log(`Final result: ${JSON.stringify(result)}`)) .catch(error => console.error(`Error: ${error.message || String(error)}`));

If you can't use an async function, it's fairly similar, but you build up a promise chain:如果你不能使用async函数,它非常相似,但你建立了一个承诺链:

function doSeriesOfThings() {
    let promise = Promise.resolve(/* the first value to pass, perhaps `undefined` or `null` */);
    for (const obj of arrayofObjs) {
        promise = promise.then(result => doSomeAsyncStuff(obj, result));
        // Or you can write it like this: `promise = promise.then(doSomeAsyncStuff);`
    }
    return promise;
}

Live Example:现场示例:

 const arrayofObjs = [ {value: 1}, {value: 2}, {value: 3}, ]; function doSomeAsyncStuff(obj, value) { console.log(`doSomeAsyncStuff(${JSON.stringify(obj)}, ${value})`); return new Promise(resolve => { setTimeout(() => { resolve(obj.value + value); }, Math.random() * 500); }); } function doSeriesOfThings() { let promise = Promise.resolve(0); for (const obj of arrayofObjs) { promise = promise.then(result => doSomeAsyncStuff(obj, result)); // Or you can write it like this: `promise = promise.then(doSomeAsyncStuff);` } return promise; } doSeriesOfThings() .then(result => console.log(`Final result: ${result}`)) .catch(error => console.error(`Error: ${error.message || String(error)}`));

And again, if you need an array of results, you can do that too:同样,如果您需要一系列结果,您也可以这样做:

function doSeriesOfThings() {
    const results = [];
    let promise = Promise.resolve(/* the first value to pass, perhaps `undefined` or `null` */);
    for (const obj of arrayofObjs) {
        promise = promise.then(result => doSomeAsyncStuff(obj, result).then(result => {
            results.push(result);
            return result;
        }));
        // Or you can write it like this: `promise = promise.then(doSomeAsyncStuff);`
    }
    return promise.then(() => results);
}

Live Example:现场示例:

 const arrayofObjs = [ {value: 1}, {value: 2}, {value: 3}, ]; function doSomeAsyncStuff(obj, value) { console.log(`doSomeAsyncStuff(${JSON.stringify(obj)}, ${value})`); return new Promise(resolve => { setTimeout(() => { resolve(obj.value + value); }, Math.random() * 500); }); } function doSeriesOfThings() { const results = []; let promise = Promise.resolve(0); for (const obj of arrayofObjs) { promise = promise.then(result => doSomeAsyncStuff(obj, result).then(result => { results.push(result); return result; })); // Or you can write it like this: `promise = promise.then(doSomeAsyncStuff);` } return promise.then(() => results); } doSeriesOfThings() .then(result => console.log(`Final result: ${JSON.stringify(result)}`)) .catch(error => console.error(`Error: ${error.message || String(error)}`));

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

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