util.promisify
вnode.js 8.x
Новый инструмент в версии для преобразования старомодныхError first callback
преобразовать вPromise
Объекты упрощают преобразование старых проектов.
До официального запуска этого инструмента у многих людей были подобные инструменты, и такиеes6-promisify,thenify,bluebird.promisify.
И многие другие отличные инструменты реализовали такие функции, помогая нам разобраться со старыми проектами, не утруждая себя использованием различных кодов.Promise
Сделайте это снова.
Общая идея реализации инструмента
Прежде всего, нам нужно объяснить общую идею реализации этого инструмента, потому что вNode
В асинхронных обратных вызовах существует соглашение:Error first
, то есть первый параметр в функции обратного вызова должен бытьError
объект, а остальные параметры - корректные данные.
Зная такое правило, инструмент очень легко реализовать: при совпадении первого параметра со значением срабатывает триггер.reject
, остальные триггерыresolve
, простой пример кода:
function util (func) {
return (...arg) => new Promise((resolve, reject) => {
func(...arg, (err, arg) => {
if (err) reject(err)
else resolve(arg)
})
})
}
- Вызов служебной функции возвращает анонимную функцию, а анонимная функция получает параметры исходной функции.
- После того, как анонимная функция называется, реальная функция называется в соответствии с этими параметрами, а
callback
. - обнаружен
err
значение, триггерreject
, срабатывают другие условияresolve
разрешение может передаваться только в одном параметре, поэтомуcallback
Нет необходимости использовать...arg
получить все возвращаемые значения
нормальное использование
Возьмем пример из официальной документации
const { promisify } = require('util')
const fs = require('fs')
const statAsync = promisify(fs.stat)
statAsync('.').then(stats => {
// 拿到了正确的数据
}, err => {
// 出现了异常
})
и потому что этоPromise
, мы можем использоватьawait
Для дальнейшего упрощения кода:
const { promisify } = require('util')
const fs = require('fs')
const statAsync = promisify(fs.stat)
// 假设在 async 函数中
try {
const stats = await statAsync('.')
// 拿到正确结果
} catch (e) {
// 出现异常
}
Использование не сильно отличается от других инструментов, мы можем легко преобразовать обратный вызов вPromise
, а затем примените его к новому проекту.
Пользовательские обещания
Есть некоторые сценарии, которые нельзя использовать напрямуюpromisify
Для конвертации есть примерно два случая:
- не последовал
Error first callback
Согласованная функция обратного вызова - Функция обратного вызова, которая возвращает несколько аргументов
Первое есть первое, и если наши условности не соблюдаются, это, скорее всего, приведет кreject
неправильное суждение, не получение правильной обратной связи.
И второе, потому чтоPromise.resolve
Можно принять только один параметр, дополнительные параметры будут проигнорированы.
Так что для достижения правильного результата нам может понадобиться вручную реализовать соответствующиеPromise
функцию, но после ее самостоятельной реализации не гарантирует, что пользователь не вызовет вашу функциюpromisify
.
так,util.promisify
также обеспечиваетSymbol
Типkey
,util.promisify.custom
.
Symbol
Каждый тип должен знать, что это уникальное значение, вотutil.prosimify
используется для указания пользовательскогоPromise
Результат преобразования используется следующим образом:
const { promisify } = require('util')
// 比如我们有一个对象,提供了一个返回多个参数的回调版本的函数
const obj = {
getData (callback) {
callback(null, 'Niko', 18) // 返回两个参数,姓名和年龄
}
}
// 这时使用promisify肯定是不行的
// 因为Promise.resolve只接收一个参数,所以我们只会得到 Niko
promisify(obj.getData)().then(console.log) // Niko
// 所以我们需要使用 promisify.custom 来自定义处理方式
obj.getData[promisify.custom] = async () => ({ name: 'Niko', age: 18 })
// 当然了,这是一个曲线救国的方式,无论如何 Promise 不会返回多个参数过来的
promisify(obj.getData)().then(console.log) // { name: 'Niko', age: 18 }
оPromise
Почему нетresolve
Несколько значений, у меня есть смелая идея, причина, которая не была проверена и насильственно объяснена: если это можетresolve
несколько значений, вы позволяетеasync
как работает функцияreturn
(просто прочитайте это предложение для развлечения, не принимайте его всерьез)
Но это должно бытьreturn
связаны, потому чтоPromise
можно связать, каждыйPromise
выполнить вthen
будет использовать возвращаемое значение в качестве новогоPromise
объектresolve
значение, вJavaScript
выхода нетreturn
несколько параметров, поэтому даже первыйPromise
Можно вернуть несколько параметров, еслиreturn
обработка будет потеряна
В использовании его очень просто настроить, и его можно назватьpromisify
добавить в функциюpromisify.custom
Соответствующая обработка может быть выполнена.
Когда последующий код вызываетpromisify
Суд будет вынесен, когда:
- Если целевая функция существует
promisify.custom
свойство, он определит его тип:- Выдает исключение, если это не исполняемая функция
- Если это исполняемая функция, она напрямую возвращает соответствующую функцию.
- Если в целевой функции нет соответствующего свойства, следуйте
Error first callback
Соглашение о создании соответствующей функции обработки и последующем возврате
добавил этоcustom
После атрибута вам не нужно беспокоиться о том, что пользователь снова вызовет вашу функцию.promisify
.
и можно проверить, присвоивcustom
функция сpromisify
Возвращаемый адрес функции находится в одном месте:
obj.getData[promisify.custom] = async () => ({ name: 'Niko', age: 18 })
// 上边的赋值为 async 函数也可以改为普通函数,只要保证这个普通函数会返回 Promise 实例即可
// 这两种方式与上边的 async 都是完全相等的
obj.getData[promisify.custom] = () => Promise.resolve({ name: 'Niko', age: 18 })
obj.getData[promisify.custom] = () => new Promise(resolve({ name: 'Niko', age: 18 }))
console.log(obj.getData[promisify.custom] === promisify(obj.getData)) // true
некоторая встроенная пользовательская обработка
В некоторых встроенных пакетах вы также можете найтиpromisify.custom
следы, такие как наиболее часто используемыеchild_process.exec
это встроеноpromisify.custom
обработка:
const { exec } = require('child_process')
const { promisify } = require('util')
console.log(typeof exec[promisify.custom]) // function
Потому что, как и в случае с планом спасения нации по кривой, упомянутым в предыдущем примере, официальная практика заключается в использовании имени параметра в сигнатуре функции какkey
, сохраните все его параметры вObject
Возвращаются объекты, такие какchild_process.exec
Возвращаемое значение отбрасываетсяerror
будет содержать два,stdout
иstderr
, один из них является правильным выводом после выполнения команды, а другой — неправильным выводом после выполнения команды:
promisify(exec)('ls').then(console.log)
// -> { stdout: 'XXX', stderr: '' }
Либо мы намеренно вводим какие-то неправильные команды, естественно, это можно сделать только вcatch
Захватить можно только под модуль, а общая команда выполняется нормально.stderr
будет пустой строкой:
promisify(exec)('lss').then(console.log, console.error)
// -> { ..., stdout: '', stderr: 'lss: command not found' }
включает в себя какsetTimeout
,setImmediate
также добился соответствующегоpromisify.custom
.
чтобы достичьsleep
операции, также вручную использоватьPromise
в упаковкеsetTimeout
:
const sleep = promisify(setTimeout)
console.log(new Date())
await sleep(1000)
console.log(new Date())
Встроенная функция посттрансформации обещаний
если вашNode
используемая версия10.x
Выше вы также можете найти похожие.promises
Подмодуль , который содержит функции обратного вызова, обычно используемые в этом модуле.Promise
версия (обаasync
функция), нет необходимости делать это вручнуюpromisify
преобразован.
И я лично считаю, что это хорошее руководство, потому что в предыдущей реализации инструмента некоторые предпочитают напрямую перезаписывать исходную функцию, а некоторые добавляют ее после имени исходной функции.Async
Чтобы отличить, официальный — это внедрить в модуль подмодуль и реализовать его внутриPromise
функция версии, на самом деле это очень удобно использовать, просто возьмитеfs
Модуль например:
// 之前引入一些 fs 相关的 API 是这样做的
const { readFile, stat } = require('fs')
// 而现在可以很简单的改为
const { readFile, stat } = require('fs').promises
// 或者
const { promises: { readFile, stat } } = require('fs')
Осталось только позвонитьpromisify
Соответствующий код можно удалить для другого использованияAPI
С точки зрения кода это изменение незаметно.
так что если вашnode
Если версия достаточно высокая, перед использованием встроенного модуля можно ознакомиться с документацией, есть ли соответствующая?promises
Поддержка, если она реализована, может быть использована напрямую.
Некоторые заметки о обещаниях
- должен встретить
Error first callback
конвенция - не может возвращать несколько параметров
- Обратите внимание, что функция, выполняющая преобразование, содержит
this
цитаты
Для первых двух вопросов используйте ранее упомянутыйpromisify.custom
можно решить.
А вот третий пункт может быть нами в некоторых случаях проигнорирован, это неpromisify
Уникальная проблема, просто очень простой пример:
const obj = {
name: 'Niko',
getName () {
return this.name
}
}
obj.getName() // Niko
const func = obj.getName
func() // undefined
Аналогично, если мы делаемPromise
При преобразовании он также похож на это, поэтому он может привести к сгенерированной функции.this
Укажите на проблему.
Есть два способа исправить такую проблему:
- Также рекомендуется использовать стрелочные функции.
- вызов
promisify
использовался раньшеbind
свяжите соответствующиеthis
Однако эта проблема также основана наpromisify
Происходит, когда преобразованная функция присваивается другой переменной.
Если это такой код, то вообще не беспокойтесьthis
Укажите на проблему:
const obj = {
name: 'Niko',
getName (callback) {
callback(null, this.name)
}
}
// 这样的操作是不需要担心 this 指向问题的
obj.XXX = promisify(obj.getName)
// 如果赋值给了其他变量,那么这里就需要注意 this 的指向了
const func = promisify(obj.getName) // 错误的 this
резюме
На мой взглядPromise
как современникjavaScript
Основная часть асинхронного программирования, узнайте, как преобразовать старый код вPromise
Это очень интересно.
А я пошел узнавать про официальный инструмент, причина в поискеRedis
СвязанныйPromise
версия видела этоreadme:
This package is no longer maintained. node_redis now includes support for promises in core, so this is no longer needed.
затем прыгнул кnode_redis
План реализации внутри, в котором упоминаетсяutil.promisify
, я схватил его и изучил, он показался мне довольно интересным, я обобщил его и поделился с вами.