it-source

지도 내에서 비동기 함수를 호출하는 가장 좋은 방법은?

criticalcode 2023. 10. 18. 22:12
반응형

지도 내에서 비동기 함수를 호출하는 가장 좋은 방법은?

배열을 매핑하고 있는데 새 개체의 반환 값 중 하나에 대해 비동기 호출을 해야 합니다.

var firebaseData = teachers.map(function(teacher) {
  return {
    name: teacher.title,
    description: teacher.body_html,
    image: urlToBase64(teacher.summary_html.match(/src="(.*?)"/)[1]),
    city: metafieldTeacherData[teacher.id].city,
    country: metafieldTeacherData[teacher.id].country,
    state: metafieldTeacherData[teacher.id].state,
    studioName: metafieldTeacherData[teacher.id].studioName,
    studioURL: metafieldTeacherData[teacher.id].studioURL
  }
});

그 기능의 구현은 다음과 같습니다.

function urlToBase64(url) {
  request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      return "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64');
    }
  });
}

어떻게 하는 게 최선의 방법인지 잘 모르겠어요약속?중첩 콜백?ES6나 ES7에서 뭔가를 사용한 다음 바벨과 바꿔치기?

현재 이를 구현하는 가장 좋은 방법은 무엇입니까?

2018년 업데이트:Promise.all맵 콜백 내의 비동기 기능은 구현하기가 더 쉽습니다.

    let firebaseData = await Promise.all(teachers.map(async teacher => {
        return {
            name: teacher.title,
            description: teacher.body_html,
            image: await urlToBase64(teacher.summary_html.match(/src="(.*?)"/)[1]),
            city: metafieldTeacherData[teacher.id].city,
            country: metafieldTeacherData[teacher.id].country,
            state: metafieldTeacherData[teacher.id].state,
            studioName: metafieldTeacherData[teacher.id].studioName,
            studioURL: metafieldTeacherData[teacher.id].studioURL
        }
    }));


async function urlToBase64(url) {
  return request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      return "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64');
    }
  });
}

Edit@2018/04/29: 저는 모든 사람들을 위한 일반적인 예를 들었습니다.

Edit@2019/06/19: 일부 요청이 실패한 경우 프로세스가 계속 작동하도록 하려면 비동기/await가 오류를 처리하기 위한 시도/catch가 있어야 합니다.

let data = await Promise.all(data.map(async (item) => {
      try {
      item.fetchItem = await fetchFunc(item.fetchParams);

      return item; 
      } catch(error) {
         return {...item, error } ;
      }
  }));
  /* we can filter errors in data and retry later 
   * eg:
   * const errorItems = data.filter(item => !!item.error)
   */
 

하나의 접근 방식은 (ES6)입니다.

이 답변은 Node 4.0+에서 작동합니다.이전 버전에는 약속 폴리필 또는 라이브러리가 필요합니다.저는 ES6 화살표 기능도 사용해 보았는데, 이 기능은 일반적인 것으로 대체가 가능합니다.function노드 < 4의 경우 s.

이 기술은 수동으로 랩핑합니다.request.get약속과 함께요청 약속 같은 도서관을 이용할 수도 있습니다.

function urlToBase64(url) {
  return new Promise((resolve, reject) => {
    request.get(url, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        resolve("data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64'));
      } else {
        reject(response);
      }
    });
  })
} 

// Map input data to an Array of Promises
let promises = input.map(element => {
  return urlToBase64(element.image)
    .then(base64 => {
      element.base64Data = base64;
      return element;
    })
});

// Wait for all Promises to complete
Promise.all(promises)
  .then(results => {
    // Handle results
  })
  .catch(e => {
    console.error(e);
  })

2020년에는 ECMAScript2021구문을 사용하여 다음과 같이 작업을 대폭 간소화할 수 있습니다.

이제 간단히 다음과 같은 작업을 수행할 수 있습니다.

//return an array of promises from our iteration:
let promises = teachers.map(async m => {
   return await request.get(....);
});

//simply iterate those
//val will be the result of the promise not the promise itself
for await (let val of promises){
   ....
}

sync.map을 사용할 수 있습니다.

var async = require('async');

async.map(teachers, mapTeacher, function(err, results){
  // results is now an array of stats for each file
});

function mapTeacher(teacher, done) {
  // computing stuff here...
  done(null, teacher);
}

모든 교사는 병렬로 처리됩니다. 다음 기능도 사용할 수 있습니다.

mapSeries(arr, iterator, [callback])일일이 지도를 그리다

mapLimit(arr, limit, iterator, [callback])지도들limit동시에

저도 비슷한 문제가 있었고 이것이 더 쉽다는 것을 알았습니다(Kai의 일반 템플릿을 사용하고 있습니다).아래에서는 하나만 사용하면 됩니다.await하고 있었습니다 저는 또한 저의 비동기 함수로서 ajax 함수를 사용하고 있었습니다.

function asyncFunction(item) {
    return $.ajax({
        type: "GET",
        url: url,
        success: response => {
            console.log("response received:", response);
            return response;
        }, error: err => {
            console.log("error in ajax", err);
        }
    });
}

let data = await Promise.all(data.map(item => asyncFunction(item)));

아래의 비동기 맵 함수인 맵()을 사용해 보십시오.

async function amap(arr,fun) {
    return await Promise.all(arr.map(async v => await fun(v)))
}

또는 보다 간결한 방식으로 작성됩니다.

let amap = async (arr,fun) => await Promise.all(arr.map(async v => await fun(v)))

용도:

let arr = await amap([1,2,3], async x => x*2)
console.log(arr)   // [2, 4, 6]

어레이 상에서 비동기 기능을 사용하고 있습니다.array.map을 사용하지 않고 a를 함수에 사용합니다.이것은 이런 것입니다.

const resultingProcessedArray = async function getSomeArray() {
    try {
      let { data } = await axios({url: '/myUrl', method:'GET'}); //initial array
      let resultingProcessedArray = [];
      for (let i = 0, len = data.items.length; i < len; i++) {
        let results = await axios({url: `/users?filter=id eq ${data.items[i].someId}`, method:'GET'});
        let domainName = results.data.items[0].domainName;
        resultingProcessedArray.push(Object.assign(data.items[i], {domainName}));
      }
      return resultingProcessedArray;
    } catch (err) {
      console.error("Unable to fetch the data", err);
      return [];
    }
};

저는 편의상 이 글을 써야 했습니다.그렇지 않으면 https://github.com/mcollina/make-promises-safe 이 필요할 수도 있습니다.

export async function mapAsync<T, U>(
  arr: T[], 
  callbackfn: (value: T, index: number, array: T[]) => Promise<U>, 
  thisArg?: any
) {
  return await Promise.all(arr.map(async (value, index, array) => {
    try {
      return await callbackfn(value, index, array);
    } catch(e) {
      throw e;
    }
  }, thisArg));
}

lodasync와 같은 lib을 사용하고자 하는 생산 목적으로 휠을 재창조해서는 안 됩니다.

import { mapAsync } from 'lodasync'

const result = await mapAsync(async(element) => {
  return 3 + await doSomething(element)
}, array)

그것은 약속을 사용하고, 의존성이 없으며, 최대한 빠릅니다.

약속을 이용해서.비동기 기능(즉, 약속)을 가진 각 작업에 대해 지도를 만들 수 있는 모든 것.

필터를 만들려면 먼저 비동기 맵(Promise.all을 사용)을 사용한 다음 true/false 값을 살펴보고 필터링/평가를 동기적으로 수행할 수 있습니다.

비동기 기능으로 오른쪽 기능줄이고 줄이려면 원래 기능을 누산기가 해결될 때까지 기다리는 새 기능으로 랩핑할 수 있습니다.

이 지식을 사용하면 원래 배열 방법을 정상/동기 기능으로 "평범한 대로" 계속 작동하지만 비동기 기능으로도 작동할 수 있도록 수정할 수 있습니다.

// a 'mini library' (save it somewhere and import it once/project)
(() => {
  let AsyncFunction = Object.getPrototypeOf(async e => e).constructor;
  ['map', 'forEach'].forEach(method => {
    let orgMethod = Array.prototype[method];
    Array.prototype[method] = function (func) {
      let a = orgMethod.call(this, func);
      return func instanceof AsyncFunction ? Promise.all(a) : a;
    };
  });
  ['filter', 'some', 'every'].forEach(method => {
    let orgMethod = Array.prototype[method];
    Array.prototype[method] = function (func) {
      if (func instanceof AsyncFunction) {
        return (async () => {
          let trueOrFalse = await this.map(func);
          return orgMethod.call(this, (_x, i) => trueOrFalse[i]);
        })();
      }
      else {
        return orgMethod.call(this, func);
      }
    };
  });
  ['reduce', 'reduceRight'].forEach(method => {
    let orgMethod = Array.prototype[method];
    Array.prototype[method] = function (...args) {
      if (args[0] instanceof AsyncFunction) {
        let orgFunc = args[0];
        args[0] = async (...args) => {
          args[0] = await args[0];
          return orgFunc.apply(this, args);
        };
      }
      return orgMethod.apply(this, args);
    };
  });
})();

// AND NOW:

// this will work
let a = [1, 2, 3].map(x => x * 3); // => [3, 6, 9]
let b = [1, 2, 3, 4, 5, 6, 7].filter(x => x > 3); // [4, 5, 6, 7]
let c = [1, 2, 3, 4, 5].reduce((acc, val) => acc + val); // => 15

// this will also work
let x = await [1, 2, 3].map(async x => x * 3);
let y = await [1, 2, 3, 4, 5, 6, 7].filter(async x => x > 3);
let z = await [1, 2, 3, 4, 5].reduce(async (acc, val) => acc + val);

모든 요소를 동시에 매핑하려면:

function asyncMap(arr, fn) {
  return Promise.all(arr.map(fn));
}

모든 요소를 동시에 매핑하지 않으려면(예: 매핑 기능에 부작용이 있거나 모든 배열 요소를 한 번에 매핑하는 경우 리소스 비용이 너무 많이 듭니다):

옵션 A: 약속

function asyncMapStrict(arr, fn) {
  return new Promise((resolve) => {
    const result = [];
    arr.reduce(
      (promise, cur, idx) => promise
        .then(() => fn(cur, idx, arr)
          .then((res) => {
            result.push(res);
          })),
      Promise.resolve(),
    ).then(() => resolve(result));
  });
}

옵션 B: 비동기/대기

async function asyncMapStrict(arr, fn) {
  const result = [];

  for (let idx = 0; idx < arr.length; idx += 1) {
    const cur = arr[idx];

    result.push(await fn(cur, idx, arr));
  }

  return result;
}

내에서 비동기 함수를 호출하는 가장 좋은 방법mapa를 사용하는 것입니다.map비동기 기능을 위해 특별히 생성되었습니다.

함수가 비동기화되려면 약속을 반환해야 합니다.

function urlToBase64(url) {
  return new Promise((resolve, reject) => {
    request.get(url, function (error, response, body) {
      if (error) {
        reject(error)
      } else if (response && response.statusCode == 200) {
        resolve(
          "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString('base64');
        )
      } else {
        reject(new Error('invalid response'))
      }
    });
  })
}

이제 매핑할 수 있습니다.

const { pipe, map, get } = require('rubico')

const metafieldTeacherData = {} // { [teacher_id]: {...}, ... }

const parseTeacher = teacher => ({
  name: teacher.title,
  description: teacher.body_html,
  image: urlToBase64(teacher.summary_html.match(/src="(.*?)"/)[1]),
  city: metafieldTeacherData[teacher.id].city,
  country: metafieldTeacherData[teacher.id].country,
  state: metafieldTeacherData[teacher.id].state,
  studioName: metafieldTeacherData[teacher.id].studioName,
  studioURL: metafieldTeacherData[teacher.id].studioURL
})

const main = async () => {
  const teachers = [] // array full of teachers
  const firebaseData = await map(pipe([
    parseTeacher,
    get('studioURL'),
    urlToBase64,
  ]))(teachers)
  console.log(firebaseData) // > ['data:application/json;base64,...', ...]
}

main()

루비코의 지도는 걱정을 합니다.Promise.all그러니 그럴 필요는 없습니다.

IIFE와 Promise를 사용합니다.모두 간단한 사용 사례를 만들 수 있습니다.

await Promise.all(arr.map(el=>(async _=>{
    // some async code       
})()))

이 IIFE는 지도 함수의 반환 값으로 사용되는 약속을 반환할 수 있습니다.

(async _=>{
    // some async code       
})()

그래서 arr.map은 Promise에게 약속 목록을 반환합니다.모두 처리할 수 있습니다.

const sleep = (ms) => {
  return new Promise((resolve, reject) => {
    setTimeout(_ => {
      resolve()
    }, ms)
  });
}

await Promise.all([1000,2000,3000].map(el=>(async _=>{
    await sleep(el)
    console.log(el) 
    return el  
})())) 

여기에 선택할 수 있는 간단한 기능이 있습니다.await각 매핑 작업(serial) 또는 모든 매핑을 실행합니다.parallel.

맵퍼 기능이 a를 반환할 필요는 없습니다.promise어느 하나.

async function asyncMap(items, mapper, options = {
  parallel: true
}) {
  const promises = items.map(async item => options.parallel ? mapper(item) : await mapper(item))
  return Promise.all(promises)
}

타이프스크립트 버전

async function asyncMap<I, O>(items: I[], mapper: (item: I) => O, options = {
  parallel: true
}): Promise<O[]> {
  const promises = items.map(async item => options.parallel ? mapper(item) : await mapper(item))
  return Promise.all(promises)
}

과학을 위한 작업 토막글

async function asyncMap(items, mapper, options = {
  parallel: true
}) {
  const promises = items.map(async item => options.parallel ? mapper(item) : await mapper(item))
  return Promise.all(promises)
}

// A test to multiply number by 2 after 50 milliseconds
function delay(num) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(num * 2)
    }, 50)
  })
}

;
(async() => {
  const data = [1, 2, 3, 4]
  const resParallel = await asyncMap(data, it => delay(it))
  const resSerial = await asyncMap(data, it => delay(it), {
    parallel: false
  })
  console.log(data)
  console.log(resParallel)
  console.log(resSerial)

})();

언급URL : https://stackoverflow.com/questions/33438158/best-way-to-call-an-asynchronous-function-within-map

반응형