9 советов по обещаниям

Node.js внешний интерфейс Программа перевода самородков Promise
9 советов по обещаниям

Как говорят коллеги, обещания преуспевают в работе.

prom

Эта статья даст вам несколько советов о том, как улучшить ваши отношения с промисами.

1. Вы можете вернуть обещание в .then

Поясню самый важный момент

Да! Вы можете вернуть Promise внутри .then

Кроме того, это Обещание, которое возвращается, будет в следующем.thenавтоматически анализируется.

.then(r => {
    return serverStatusPromise(r); // 返回 { statusCode: 200 } 的 Promise
})
.then(resp => {
    console.log(resp.statusCode); // 200; 注意自动解析的 promise
})

2. Новое обещание автоматически создается каждый раз, когда выполняется .then.

Если вы знакомы со стилем цепочки javascript, он должен показаться вам знакомым. Но для новичка, наверное, нет.

В Promises независимо от того, что вы используете.thenили.catchсоздаст новое обещание. Это Обещание - это Обещание, только что связанное и только что добавленное..then / .catchКомбинация.

Давайте посмотрим на один 🌰:

var statusProm = fetchServerStatus();

var promA = statusProm.then(r => (r.statusCode === 200 ? "good" : "bad"));

var promB = promA.then(r => (r === "good" ? "ALL OK" : "NOTOK"));

var promC = statusProm.then(r => fetchThisAnotherThing());

Взаимосвязь вышеупомянутого промиса можно четко описать на блок-схеме:

image

Особо следует отметить, чтоpromA,promBа такжеpromCВсе разные, но связанные промисы.

мне нравится ставить.thenПодумайте о большом трубопроводе, по которому вода перестает течь вниз по течению, когда вышестоящий узел выходит из строя. Например, еслиpromBвыходит из строя, нижестоящие узлы не затрагиваются, но еслиstatusPromвыходит из строя, то будут затронуты все нижестоящие узлы, т.е.rejected.

3. Для вызывающего абонентаPromiseизresolved/rejectedсостояние уникально

Я думаю, что это одна из самых важных вещей, чтобы обещания работали хорошо. Проще говоря, если промисы используются многими разными модулями в вашем приложении, то когда промисы возвращаютсяresolved/rejectedсостояние, все вызывающие абоненты будут уведомлены.

Это также означает, что никто не может изменить ваше Обещание, поэтому его можно безопасно передать.

function yourFunc() {
  const yourAwesomeProm = makeMeProm();

  yourEvilUncle(yourAwesomeProm); // 无论 Promise 受到了怎样的影响,它最终都会成功执行

  return yourAwesomeProm.then(r => importantProcessing(r));
}

function yourEvilUncle(prom) {
  return prom.then(r => Promise.reject("destroy!!")); // 可能遭受的影响
}

Как видно из приведенного выше примера, обещания спроектированы таким образом, чтобы их было трудно изменить. Как я сказал выше: «Сохраняйте спокойствие и передайте Обещание».

4. Конструкторы промисов — не решение

Я вижу, что многим разработчикам нравится стиль конструктора, они думают, что таковы и промисы. Но это ложь, на самом деле причина в том, что API конструктора похоже на API предыдущей функции обратного вызова, и такие привычки трудно изменить.

Если вы обнаружите, что используете вездеPromise 构造函数, то ваш подход неверен!

Чтобы действительно сделать шаг вперед и избавиться от обратных вызовов, нужно быть осторожным и использовать конструктор Promise по минимуму.

Давайте посмотрим на использованиеPromise 构造函数Конкретная ситуация:

return new Promise((res, rej) => {
  fs.readFile("/etc/passwd", function(err, data) {
    if (err) return rej(err);
    return res(data);
  });
});

Promise 构造函数долженИспользуйте, только если вы хотите преобразовать обратные вызовы в обещания. Как только вы освоите этот элегантный способ создания обещаний, он станет очень привлекательным.

давайте посмотрим на лишнееPromise 构造函数.

☠️Неправильно

return new Promise((res, rej) => {
    var fetchPromise = fetchSomeData(.....);
    fetchPromise
        .then(data => {
            res(data); // 错误!!!
        })
        .catch(err => rej(err))
})

💖правильный

return fetchSomeData(...); // 正确的!

использоватьPromise 构造函数Обертывание обещанияизлишним и противоречит цели самого обещания.

😎Расширенные советы

если выnodejsРазработчики, предлагаю посмотретьutil.promisify. Этот метод помогает вам преобразовать обратные вызовы в стиле узла в обещания.

const {promisify} = require('util');
const fs = require('fs');

const readFileAsync = promisify(fs.readFile);

readFileAsync('myfile.txt', 'utf-8')
  .then(r => console.log(r))
  .catch(e => console.error(e));

5. Использование обещания

Javascript предоставляетPromise.resolveметод, такой же краткий, как следующий пример:

var similarProm = new Promise(res => res(5));
// ^^ 等价于
var prom = Promise.resolve(5);

Он имеет несколько вариантов использования, один из моих любимых — преобразование обычных (асинхронных) объектов js в промисы.

// 将同步函数转换为异步函数
function foo() {
  return Promise.resolve(5);
}

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

function goodProm(maybePromise) {
  return Promise.resolve(maybePromise);
}

goodProm(5).then(console.log); // 5

var sixPromise = fetchMeNumber(6);

goodProm(sixPromise).then(console.log); // 6

goodProm(Promise.resolve(Promise.resolve(5))).then(console.log); // 5, 注意,它会自动解析所有的 Promise!

6. Используйте Promise.reject

Javascript также обеспечиваетPromise.rejectметод. Так же кратко, как следующий пример:

var rejProm = new Promise((res, reject) => reject(5));

rejProm.catch(e => console.log(e)) // 5

Мое любимое использование - использовать заранееPromise.rejectотказаться.

function foo(myVal) {
    if (!mVal) {
        return Promise.reject(new Error('myVal is required'))
    }
    return new Promise((res, rej) => {
        // 从你的大回调到 Promise 的转换!
    })
}

Проще говоря, используйтеPromise.rejectВы можете отклонить любое Обещание, которое хотите отклонить.

В приведенном ниже примере я.thenВнутреннее использование:

.then(val => {
  if (val != 5) {
    return Promise.reject('Not Good');
  }
})
.catch(e => console.log(e)) // 这样是不好的

Примечание: вы можете сделать что-то вродеPromise.resolveтоже самоеPromise.rejectпередать любое значение. Что вы часто находите в невыполненных обещанияхErrorПричина в том, что он в основном используется для выдачи асинхронной ошибки.

7. Использование Promise.all

Javascript предоставляетPromise.allметод. Как ... это краткое, ну, я не могу придумать пример 😁.

В псевдоалгоритмеPromise.allможно резюмировать как:

接收一个 Promise 数组

    然后同时运行他们

    然后等到他们全部运行完成

    然后 return 一个新的 Promise 数组

    他们其中有一个失败或者 reject,都可以被捕获。

В следующем примере показаны все выполненные промисы:

var prom1 = Promise.resolve(5);
var prom2 = fetchServerStatus(); // 返回 {statusCode: 200} 的 Promise

Proimise.all([prom1, prom2])
.then([val1, val2] => { // 注意,这里被解析成一个数组
    console.log(val1); // 5
    console.log(val2.statusCode); // 200
})

В следующем примере показано, что происходит, когда один из них выходит из строя:

var prom1 = Promise.reject(5);
var prom2 = fetchServerStatus(); // 返回 {statusCode: 200} 的 Promise

Proimise.all([prom1, prom2])
.then([val1, val2] => {
    console.log(val1); 
    console.log(val2.statusCode); 
})
.catch(e =>  console.log(e)) // 5, 直接跳转到 .catch

Уведомление:Promise.allочень умный! Если одно из обещаний не выполняется, оно не ждет, пока все обещания будут выполнены, а немедленно прерывается!

8. Не бойтесь отклонять, и не добавляйте избыточности в каждый .потом обратно.catch

Мы не часто беспокоимся о том, что ошибки проглатываются где-то между ними?

Чтобы преодолеть этот страх, вот простой совет:

Позвольте отклонению обрабатывать проблемы с вышестоящими функциями.

В идеале метод отклонения должен быть корнем приложения, и все отклонения будут передаваться вниз.

Не бойтесь писать, как показано ниже

return fetchSomeData(...);

Теперь, если вы хотите обработать случай отклонения в функции, решите, следует ли исправить проблему или продолжить отклонение.

💘разрешить отклонить

Разрешить отклонение просто, в.catchВсе, что вы вернете, будет считаться решенным. Однако, если вы.catchвернуться вPromise.reject, то обещание не будет выполнено.

.then(() => 5.length) // <-- 这里会报错
.catch(e => {
        return 5;  // <-- 重新使方法正常运行
})
.then(r => {
    console.log(r); // 5
})
.catch(e => {
    console.error(e); // 这个方法永远不会被调用 :)
})

💔отклонить отказ

Отказ от отказа прост.Ничего не нужно делать.Как я только что сказал, сделайте это проблемой с другими функциями. Обычно родительская функция имеет лучший способ обработки отклонения, чем текущая функция.

Важно помнить, что когда вы пишете метод catch, вы имеете дело с ошибкой. это и синхронизироватьtry/catchработает аналогично.

Если вы хотите перехватить отклонение: (я настоятельно не рекомендую этого делать!)

.then(() => 5.length) // <-- 这里会报错
.catch(e => {
  errorLogger(e); // 做一些错误处理
  return Promise.reject(e); // 拒绝它,是的,你可以这么做!
})
.then(r => {
    console.log(r); // 这个 .then (或者任何后面的 .then) 将永远不会被调用,因为我们在上面使用了 reject :)
})
.catch(e => {
    console.error(e); //<-- 它变成了这个 catch 方法的问题
})

Разделительная линия между .then(x,y) и then(x).catch(x)

.then接收的第二个回调函数参数也可以用来处理错误。 И этоthen(x).catch(x)Это выглядит похоже, но разница в том, как они обрабатывают ошибки, заключается в ошибках, которые они ловят сами.

Я проиллюстрирую это следующим примером:

.then(function() {
   return Promise.reject(new Error('something wrong happened'));
}).catch(function(e) {
   console.error(e); // something wrong happened
});

.then(function() {
   return Promise.reject(new Error('something wrong happened'));
}, function(e) { // 这个回调处理来自当前 `.then` 方法之前的错误
    console.error(e); // 没有错误被打印出来
});

Если вы хотите обрабатывать Promise из апстрима, а не только из.thenПри добавлении к нему ошибки.then(x,y)Стало очень удобно.

Совет: в 99,9% случаев используются простыеthen(x).catch(x)лучше.

9. Избегайте .then callback hell

Этот совет относительно прост, старайтесь избегать.thenсодержит.thenили.catch. Поверьте, этого легче избежать, чем вы думаете.

☠️Неправильно

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts)
           .then(r => r.text())
           .catch(err2 => console.error(err2))
  }
})

💖правильный

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts);
  }
})
.then(r => r.text())
.catch(err => console.erro(err));

иногда мы.thenВам нужно много переменных внутри, тогда у вас нет выбора, кроме как создать еще одну.thenцепочка методов.

.then(myVal => {
    const promA = foo(myVal);
    const promB = anotherPromMake(myVal);
    return promA
          .then(valA => {
              return promB.then(valB => hungryFunc(valA, valB)); // 很丑陋!
          })
})

Я рекомендую использовать метод деструктурирования ES6, смешанный сPromise.allспособ может решить эту проблему.

.then(myVal => {
    const promA = foo(myVal);
    const promB = anotherPromMake(myVal);
    return Promise.all([prom, anotherProm])
})
.then(([valA, valB]) => {   // 很好的使用 ES6 解构
    console.log(valA, valB) // 所有解析后的值
    return hungryFunc(valA, valB)
})

Примечание. Если ваш узел / браузер / босс / Осознание позволяет использовать, вы можете использоватьasync/awaitметод решения этой проблемы.

Я очень надеюсь, что эта статья помогла вам понять промисы.

Пожалуйста, ознакомьтесь с моим предыдущим сообщением в блоге.

Если вы ❤️ этот пост, пожалуйста, поделитесь этим постом, чтобы распространить его.

Свяжитесь со мной в Twitter@kushan2020.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.