Начиная с проекта, зачем использовать async/await вместо Promise?

внешний интерфейс API Promise

прочитать статью перед«6 причин, по которым Async/Await заменяет обещания»,Сейчасasync / awaitграмматика уже естьЭтап 3.

совместимость

  • На стороне сервера, вNode.js 7.6После версии,async / awaitграмматика былаNode.jsподдержка, напримерKoa2заброшенныйсинтаксис генератора/доходности,Объятиеasync / awaitграмматика.
  • На стороне клиента также можноBabelДавайте использовать последние, как нам нравитсяEcmascriptграмматика.

Начните с бизнеса

Лично мне не нравится использовать такой последний синтаксис. Всегда пишите код или обнимайтеPromiseосновной. Но так как использованиеasync / awaitПосле грамматики вроде бы есть ощущение зависимости от этой грамматики (похоже на код синхронной блокировки, и не надо быть похожимgeneratorсинтаксис для выпуска вручную или использованияcoтакая библиотека управления потоком)

Взгляните на эти распространенные бизнес-сценарии ниже:

  1. Предположим, мы пишем программу для написания вопросов.Эта программа умная (да, она вас понимает).Каждый раз, когда вы заполняете вопрос и отправляете его, сервер будет судить о том, освоили ли вы текущий балл знаний по отправленным вами результатам .., если вы его не осилите, то сервер снова подтолкнет вам следующий вопрос.
  2. Предположим, мы пишем программу, в которой пользователи вручную идентифицируют изображения, а сервер дает два интерфейса,интерфейс отправкиИспользуется для отправки результата идентификации предыдущего изображения, только еслиинтерфейс отправкиОтправка прошла успешно, можно перейти к запросуинтерфейс следующий, чтобы запросить ссылку на следующее изображение.
const requestSubmit = () => {
  return new Promise((resolve, reject) => {
    $.ajax({
      url: '/submit',
      success: (data) => {
        resolve(data);
      },
      error: (err) => {
        reject(err);
      },
    });
  });
};

const requestNext = () => {
  return new Promise((resolve, reject) => {
    $.ajax({
      url: '/next',
      success: (data) => {
        resolve(data);
      },
      error: (err) => {
        reject(err);
      },
    });
  });
};

Если мы используемPromiseграмматика:

requestSubmit()
  .then((data) => {
    // dosomething
    return requestNext();
  })
  .then((data) => {
    // dosomething
  })
  .catch((err) => {
    console.log(err);
  });

Хотя с помощьюPromise,catchтолько самое последнееPromise.thenошибка, но на самом деле теперь вы можете отловить каждую ошибку вот так!

не забудьPromise.thenВторой параметр:

requestSubmit()
  .then((data) => {
    // dosomething
    return requestNext();
  }, (err) => {
    console.log(err);
  })
  .then((data) => {
    // dosomething
  },(err) => {
    console.log(err);
  });

Теперь есть проблема, сервер может проверить статус входа пользователя, правильность параметров и т.д. Если статус входа пользователя недействителен, первыйajaxпросьба, все равно пойдетsuccessОбратный вызов не вызовет ошибку, вам все равно придется изменить его вручную в это время.requestSubmitфункция.

также,Promise.catchМетод перехватывает только самую последнюю ошибку, поэтому неизвестно, где именно возникла ошибка.

Такие как:

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => {
      throw new Error("oops");
    })
}
makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
  })

Но обратите внимание: исходный текст не полный, мы захватываем его такPromiseОшибка в порядке:

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise(), err => {
    console.log(err);
    // output
    // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
  })
    .then(() => callAPromise(), err => {
    console.log(err);
  })
    .then(() => callAPromise(), err => {
    console.log(err);
  })
    .then(() => callAPromise(), err => {
    console.log(err);
  })
    .then(() => {
      throw new Error("oops");
    }, err => {
    console.log(err);
  });
}
makeRequest();

Если приведенный выше бизнес-сценарий изменить наasync / awaitграмматика:

(async function() {
  try {
    const res1 = await axios.post('/submit');
    // dosomething
  } catch (err) {
    console.log(err);
  } finally {
    console.log('go next!');
  }

  try {
    const res2 = await axios.get('/next');
    // dosomething
  } catch (err) {
    console.log(err);
  } finally {
    console.log('done!');
  }
})();

Он чувствует себя лучше как с точки зрения перехвата исключений, так и с точки зрения читабельности кода.

Преимущество

На самом деле, большинство из них подхватывают эту статью:

лаконичный

использоватьasync / awaitЗначительная экономия кода. нам не нужно писать.then, не нужно писать анонимную обработку функцийPromiseизresolveзначение, нет необходимости определять избыточные переменные данных и избегать вложенного кода. Эти небольшие преимущества быстро складываются, что станет более очевидным в следующих примерах кода.

обработка ошибок

async / awaitПозволятьtry / catchМожно обрабатывать как синхронные, так и асинхронные ошибки. В следующихPromiseВ примереtry / catchНе выдерживаюJSON.parseошибка, потому что это вPromiseсередина. нам нужно использовать.catch, поэтому код обработки ошибок очень избыточен. И в нашем реальном производстве код будет более сложным.

const makeRequest = () => {
  try {
    getJSON()
      .then(result => {
        // JSON.parse可能会出错
        const data = JSON.parse(result)
        console.log(data)
      })
      // 取消注释,处理异步代码的错误
      // .catch((err) => {
      //   console.log(err)
      // })
  } catch (err) {
    console.log(err)
  }
}

использоватьasync / awaitесли,catchможет справитьсяJSON.parseошибка:

const makeRequest = async () => {
  try {
    // this parse may fail
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}

Условные операторы

Об этом также сказано выше:

В следующем примере вам нужно получить данные, а затем решить, возвращаться ли напрямую или продолжать получать дополнительные данные в соответствии с возвращенными данными.

const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}

Эти коды - головная боль, чтобы смотреть на них. вложенные (6 уровней), скобки,returnЗаявления могут легко потеряться, им просто нужно передать окончательный результат в самый внешнийPromise.

В приведенном выше коде используетсяasync / awaitНаписание может значительно улучшить читаемость:

const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data    
  }
}

медиана

Вы, вероятно, сталкивались со сценарием, в котором вызывается promise1, вызывается promise2 с результатом, возвращаемым promise1, а promise3 вызывается с результатом обоих. Ваш код, скорее всего, будет выглядеть так:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      return promise2(value1)
        .then(value2 => {        
          return promise3(value1, value2)
        })
    })
}

еслиpromise3ненужныйvalue1, легко конвертироватьpromiseВложенное мощение. Если вы терпеть не можете вложенность, вы можете поставитьvalue 1 & 2вставитьPromise.allЧтобы избежать глубокой вложенности:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      return Promise.all([value1, promise2(value1)])
    })
    .then(([value1, value2]) => {      
      return promise3(value1, value2)
    })
}

стек ошибок

Здесь вы можете увидеть реальный бизнес-пример выше.

отладка

async/awaitМожет упростить отладку кода. 2 причины сделать отладкуPromiseстановится очень больно:

  • Вы не можете устанавливать точки останова в стрелочных функциях, которые возвращают выражения

  • если ты.thenЧтобы установить точки останова в блоках кода, используйтеStep OverЯрлыки, отладчик не переходит к следующему.then, так как он будет пропускать только асинхронный код. использоватьawait / async, вам больше не нужно столько стрелочных функций, так что вы можете пропустить, например, отладку синхронного кодаawaitутверждение.