Как заставить функцию возвращать обещание, если необязательный обратный вызов не передан?

Я мог придумать

 function squareAsync(val, callback) {
if (callback) {
    setTimeout(() => {
        if (Math.random() < 0.5) {
            callback(undefined, val * val);
        }
        else {
            callback(new Error('Failed!'));
        }
    }, 2000);
}
else {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() < 0.5) {
                resolve(val * val);
            }
            else {
                reject(new Error('Failed!'));
            }
        }, 2000);
   });
 }
}

Я нашел другой способ для этого

 function squareAsync1(val, callback) {
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (Math.random() < 0.5) {
            resolve(val * val);
        }
        else {
            reject(new Error('Failed!'));
        }
    }, 2000);
});
if (callback) {
    p.then(d => {
        callback(undefined, d);
    }, e => {
        callback(e);
    });
 }
return p;
}

Какой из них лучше или есть более стандартный и элегантный способ сделать это? Можем ли мы сделать это с помощью async/await?

почему вы вызываете обратный вызов с неопределенным в качестве первого параметра?

marcelotokarnia 03.08.2018 06:23

@marcelotokarnia в случае успеха, когда ошибки нет, я передаю его как undefined, так как я предпочитаю его вместо null.

vibhor1997a 03.08.2018 06:24

кроме того, второй подход немного лучше, потому что в первом иногда функция возвращает обещание, а иногда - null.

marcelotokarnia 03.08.2018 06:25

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

marcelotokarnia 03.08.2018 06:27

Оооо, я понял, ваш первый параметр - это ошибка, а второй - значение :) ваш обратный вызов работает как для успеха, так и для ошибки ... Я понимаю ... имеет смысл :)

marcelotokarnia 03.08.2018 06:27

@marcelotokarnia да, чтобы проиллюстрировать это resolves/rejects случайным образом :)

vibhor1997a 03.08.2018 06:30

Вторая версия избегает дублирования кода, поэтому она более СУХАЯ. Кроме этого, рекомендовать то или другое нечего.

Barmar 03.08.2018 08:14
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
7
818
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

 async function squareAsync1(val, callback) {
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (Math.random() < 0.5) {
            resolve(val * val);
        }
        else {
            reject(new Error('Failed!'));
        }
    }, 2000);
});
if (callback) {
    return p.then(d => {
        return callback(undefined, d);
    }, e => {
        return callback(e);
    });
 }
return p;
}

Да, ваше решение будет работать с async / await. Обратите внимание, я только что добавил return в p.then

Таким образом вы можете сделать что-то вроде:

const x = await squareAsync1(2, (e, v) => e ? 1 : v * 2)

И вы получите x как 1 (если обещание было отклонено) или 8 (если обещание было успешным).

Ответ принят как подходящий

Сделать это можно так:

function squareAsync(val, callback) {
  const timeout = function(res, rej){
    setTimeout(function(){
      if (Math.random() < 0.5)
        res(val*val);
      else
        rej(new Error('Failed!'));
    }, 2000);
  }
  
  return typeof callback === 'function'
    ? timeout(callback.bind(null, undefined), callback)
    : new Promise(timeout);
}

// CALLBACK EXAMPLE
squareAsync(5, (err, val) => {
  if (err)
    console.info(`Callback: ${err}`);
  else
    console.info(`Callback: ${val}`);
})

// PROMISE EXAMPLE
squareAsync(5)
  .then(val => console.info(`Promise: ${val}`))
  .catch(err => console.info(`Promise: ${err}`))

Объяснение

  1. Оберните вызов setTimeout в одну функцию-оболочку timeout, чтобы вам не приходилось повторять почти идентичный код.
  2. Пусть функция timeout принимает два аргумента: res и rej(разрешить и отклонить)
  3. Вернуть timeout, если обратный вызов передан с функцией, иначе вернуть new Promise(timeout).

Теперь о том, что происходит в:

return typeof callback === 'function'
  ? timeout(callback.bind(null, undefined), callback)
  : new Promise(timeout);

Это переводится как:

if (typeof callback === 'function'){
  // Bind `null` as `this` value to `callback
  // and `undefined` as its first argument (because no error).
  // Need to to this because in `timeout` function,
  // we call `res` with only 1 argument (computed value) if success.
  const resolve = callback.bind(null, undefined);

  // Don't need to bind anything
  // because the first argument should be error.
  const reject = callback;

  // Call the function as if we are in a Promise
  return timeout(resolve, reject);
}

// Use `timeout` function as normal promise callback.
return new Promise(timeout);

Надеюсь, вы понимаете. Не стесняйтесь комментировать, если запутались.

Подробнее о привязке.

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

marcelotokarnia 03.08.2018 14:18

Другие вопросы по теме