Рабочий раствор
Ваша проблема в том, что waitSec
находится в другом контексте, чем остальная часть кода. Вам нужно поднять его до контекста Promise.
Тогда у вас есть «обещанный» код, и вы можете связывать обещания. Вот это работает (дальнейшее объяснение ниже):
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise(resolve => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
function whosOnFirst(i) {
return waitSec("who's on first", 1)
}
// What's on Second - run after completion of whosOnFirst() using return for param
function whatsOnSecond(i) {
return waitSec("what's on second", i)
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
function iDunnosOnthird(i) {
return waitSec("iDunno's on third", i)
}
whosOnFirst(1)
.then(whatsOnSecond)
.then(iDunnosOnthird)
.then(console.log)
Дальнейшее объяснение
Ваша первоначальная реализация waitSec
всегда возвращает undefined
вызывающей стороне:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
// return undefined happens here
}
Возврат внутри setTimeout
не возвращается ваш код вызова, потому что ваш код никогда не вызывает эту функцию . Он вызывается с помощью кода таймера JS асинхронно.
Возвращение значений из асинхронного кода c с использованием обратного вызова
Способ сообщить результат оттуда вызывающей стороне: либо возьмите обратный вызов в качестве параметра для внешней функции и вызовите этот обратный вызов в функции asyn c, например:
function waitSec({callerName, pause, callback}) {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
callback(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
}
В этом случае у вас есть шаблон обратного вызова classi c JS.
Использование обещаний и почему вы можете их предпочесть
Или вы возвращаете обещание и разрешаете его, чтобы вернуть значение - как я продемонстрировал в решении. Чтобы узнать больше об этом, поищите «как мне обещать обратный вызов».
Преимущество выполнения этого способа (с обещанием) состоит в том, что обещания являются составными, как показано в решении.
Обещания также возможны await
, поэтому вы можете использовать async
/ await
с функциями, возвращающими обещание.
Вам не нужно отмечать функции, возвращающие обещание, как async
.
Если вы помечаете функцию как async
, она возвращает Обещание, например:
// A function marked async returns a Promise
async function simple() {
return 3
}
// Proof that it is a Promise
simple().then(console.log)
// The equivalent code explicitly returning a Promise - this is what `async` does
function simpleP(n = 0) {
return new Promise(resolve => resolve(n+1))
}
// Proof that it behaves the same
simpleP().then(console.log)
// Promise composition
simple()
.then(simpleP)
.then(console.log)
// Same functionality, using async/await
async function main() {
const num = await simple()
const res = await simpleP(num)
console.log(`${num} + 1 = ${res}`)
}
main()
Маркировка функций синхронного возврата async
делает их компонуемыми с помощью асинхронного c кода, который на самом деле нуждается в Promise для возврата значения. Вы можете использовать его, чтобы поднять ваши функции в тот же контекст asyn c и составить их.
// Synchronous function that is Promisified and composable
async function simple() {
return 3
}
Чтобы фактически получить значение из асинхронной функции, которая принимает обратный вызов и обеспечивает его возврат через него, вы должны создать и вернуть Promise из своей функции, а затем вызвать Promise resolve
внутри функции обратного вызова, которая предоставляет значение, которое вы хотите передать.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
В этом случае вам необходимо явно подключить до машины обещания. Это практически единственный раз, когда вам нужно создать и вернуть Обещание.
То, что он делает - это оборачивает интерфейс обратного вызова для вас.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
Обратите внимание, что в примере обратного вызова вы должны были взять аргумент callback
от вызывающей стороны в качестве параметра, а затем вызвать его с возвращаемым значением внутри обратного вызова, который предоставляет значение.
С механизмом Promise вы принимаете аргумент обратного вызова, но он не предоставляется вызывающей стороной, он предоставляется созданным вами Promise. А затем вы возвращаете обещание вызывающей стороне.
Вышеприведенная функция getDBRecordP
представляет собой суть «как преобразовать функцию, которая принимает обратный вызов, в функцию, которая возвращает обещание?».
Затем вызывающая сторона передает обратный вызов методу Promise.then()
.
Таким образом, у вас есть машина, которая оборачивает соглашение о передаче обратного вызова в формальный API, который можно компоновать, а обратные вызовы - нет.
Другим аспектом машины Promise является метод .catch()
.
Принимая во внимание, что с обратными вызовами всегда было принято, что подпись обратного вызова (err, result)
, машина Promise позволяет вам предоставить два обратных вызовов - один для err
и один для result
.