【问题标题】:How to use the result of first promise in the second and so on in Promise.all如何在 Promise.all 中使用第一个 promise 的结果,以此类推
【发布时间】:2020-09-11 08:15:59
【问题描述】:

通过遵循良好的answer by T.J. Crowder 到 SO 线程,我设法将异步任务循环与 Promise.all 结合起来。实际问题是,首先我想在 Promisified 函数中读取一个 excel 文件,并在第二个 Promisified 函数中读取图像文件列表。 这是执行文件读取的代码函数。

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

这里是执行一个接一个读取文件的动作代码。

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

【问题讨论】:

  • 我认为您不应该在这种情况下使用 Promise.all,因为无论如何您都在等待承诺在链中完成
  • 听起来你不想使用Promise.all,因为它会并行运行所有承诺。为什么不把它们锁起来?每个都依赖于前一个,不是吗?所以promise1().then(promise2).then(promise3)等
  • 哦,你想串联(一个接一个)运行promise,而不是并行(同时)?似乎是这样,因为您希望 Promise 1 的结果可用于 Promise 2 ...等
  • 什么是arrayofObjs,您在哪里调用doSomeAsyncStuff(arrayofObjs) - 和resolve(anArrayofObjs); 中的anArrayofObjs - 不清楚

标签: javascript reactjs typescript promise


【解决方案1】:

My answer to this other question 接近于回答这个问题,但我不确定它是否完全如此。

由于您想在第二个操作中使用第一个操作的结果,在第三个操作中使用第二个操作的结果,等等,您不能并行运行异步操作。所以你必须连续运行它们。

如果你可以使用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;
}

现场示例:

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

如果您还需要一个结果数组,只需在函数中构建它:

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

现场示例:

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

如果你不能使用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;
}

现场示例:

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

同样,如果您需要一组结果,您也可以这样做:

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

现场示例:

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

【讨论】:

  • 好的,谢谢。我在 doAsyncStuff 中所做的类似于 new Promise((resolve) => {const reader = new FileReader(); reader.onload = .... ; reader.readAsDataURL(file)}}
  • Promise 中的这个文件读取操作是否会导致任何问题。目前我的承诺在所有文件的读取完成之前就解决了......
  • @DevLoverUmar - 你没有理由不能使用FileReader。你还没有展示你是如何打电话给resolve,但从你所说的我怀疑你遇到了this problem。你会想要:return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = () => reject(reader.error); reader.readXYZ(); }; 但在现代环境中,请改用Blob 上的方法,例如text()arrayBuffer()
  • 我用真实代码更新了这个问题。如果你有时间请看一下。很抱歉给您带来不便
【解决方案2】:

如果你想使用循环来链接 Promise,你需要 asyncawait

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)

【讨论】:

    【解决方案3】:

    您可以尝试将返回 Promise 而不是 Promise 的函数推送到您的数组中。这样你就可以在最后一个 Promise 的数据实际可用时调用它们。

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

    在上面的示例中,我尝试使代码尽可能接近您的原始代码。但是我冒昧地重新设计了函数回调以接受链中的任何函数而不是单个函数。

    如果你反对使用async/await,使用普通的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);

    【讨论】:

      猜你喜欢
      • 2020-08-06
      • 1970-01-01
      • 2019-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-05
      相关资源
      最近更新 更多