【问题标题】:javascript : Async/await in .replacejavascript:.replace 中的异步/等待
【发布时间】:2016-02-11 09:25:17
【问题描述】:

我正在通过以下方式使用 async/await 函数

async function(){
  let output = await string.replace(regex, async (match)=>{
    let data = await someFunction(match)
    console.log(data); //gives correct data
    return data
  })
  return output;
}

但是返回的数据是一个promise对象。只是对在此类带有回调的函数中实现它的方式感到困惑。

【问题讨论】:

  • async 函数的返回值始终是一个 Promise 对象,该对象使用返回的 output 进行解析(或拒绝并抛出错误)。
  • 您想知道为什么output 是一个承诺吗?我不清楚你的问题是什么。请注意,如果string.replace 的字面意思是String.prototype.replace,那么这是行不通的。 .replace 期望回调是普通函数,而不是异步函数。

标签: javascript async-await es6-promise ecmascript-2016


【解决方案1】:

一个易于使用和理解的异步替换功能:

async function replaceAsync(str, regex, asyncFn) {
    const promises = [];
    str.replace(regex, (match, ...args) => {
        const promise = asyncFn(match, ...args);
        promises.push(promise);
    });
    const data = await Promise.all(promises);
    return str.replace(regex, () => data.shift());
}

它会执行两次替换功能,因此请注意您是否需要进行繁重的处理。不过,对于大多数用途来说,它非常方便。

像这样使用它:

replaceAsync(myString, /someregex/g, myAsyncFn)
    .then(replacedString => console.log(replacedString))

或者这个:

const replacedString = await replaceAsync(myString, /someregex/g, myAsyncFn);

不要忘记您的myAsyncFn 必须返回一个承诺。

asyncFunction 的一个例子:

async function myAsyncFn(match) {
    // match is an url for example.
    const fetchedJson = await fetch(match).then(r => r.json());
    return fetchedJson['date'];
}

function myAsyncFn(match) {
    // match is a file
    return new Promise((resolve, reject) => {
        fs.readFile(match, (err, data) => {
            if (err) return reject(err);
            resolve(data.toString())
        });
    });
}

【讨论】:

  • 这仅在使用 replace 迭代匹配项时有效。这不适用于替换。
  • 确实如此。它迭代和替换。
  • 我真的很喜欢这个解决方案,又好又简单!
【解决方案2】:

native replace method 不处理异步回调,您不能将它与返回承诺的替换器一起使用。

不过,我们可以编写自己的 replace 函数来处理 Promise:

async function(){
  return string.replace(regex, async (match)=>{
    let data = await someFunction(match)
    console.log(data); //gives correct data
    return data;
  })
}

function replaceAsync(str, re, callback) {
    // http://es5.github.io/#x15.5.4.11
    str = String(str);
    var parts = [],
        i = 0;
    if (Object.prototype.toString.call(re) == "[object RegExp]") {
        if (re.global)
            re.lastIndex = i;
        var m;
        while (m = re.exec(str)) {
            var args = m.concat([m.index, m.input]);
            parts.push(str.slice(i, m.index), callback.apply(null, args));
            i = re.lastIndex;
            if (!re.global)
                break; // for non-global regexes only take the first match
            if (m[0].length == 0)
                re.lastIndex++;
        }
    } else {
        re = String(re);
        i = str.indexOf(re);
        parts.push(str.slice(0, i), callback.apply(null, [re, i, str]));
        i += re.length;
    }
    parts.push(str.slice(i));
    return Promise.all(parts).then(function(strings) {
        return strings.join("");
    });
}

【讨论】:

    【解决方案3】:

    因此,没有需要承诺的替换过载。因此,只需重述您的代码:

    async function(){
      let data = await someFunction();
      let output = string.replace(regex, data)
      return output;
    }
    

    当然,如果你需要使用匹配值传递给异步函数,事情会变得有点复杂:

    var sourceString = "sheepfoohelloworldgoocat";
    var rx = /.o+/g;
    
    var matches = [];
    var mtch;
    rx.lastIndex = 0; //play it safe... this regex might have state if it's reused
    while((mtch = rx.exec(sourceString)) != null)
    {
        //gather all of the matches up-front
        matches.push(mtch);
    }
    //now apply async function someFunction to each match
    var promises = matches.map(m => someFunction(m));
    //so we have an array of promises to wait for...
    //you might prefer a loop with await in it so that
    //you don't hit up your async resource with all
    //these values in one big thrash...
    var values = await Promise.all(promises);
    //split the source string by the regex,
    //so we have an array of the parts that weren't matched
    var parts = sourceString.split(rx);
    //now let's weave all the parts back together...
    var outputArray = [];
    outputArray.push(parts[0]);
    values.forEach((v, i) => {
        outputArray.push(v);
        outputArray.push(parts[i + 1]);
    });
    //then join them back to a string... voila!
    var result = outputArray.join("");
    

    【讨论】:

    • 我已经更新了这个问题。我需要将匹配的元素传递给函数,所以这种方式无法完成。
    • @ritz078 我以为你可能错过了。也许我的编辑更有用?
    猜你喜欢
    • 2018-07-01
    • 1970-01-01
    • 2019-10-01
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 2022-09-30
    • 2019-10-21
    相关资源
    最近更新 更多