Простое обещание, а затем реализация - PullRequest
7 голосов
/ 05 апреля 2019

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

Я очень наивно пытался реализовать Обещание только для того, чтобы заставить код работать.Создал ctor, который принимает функцию распознавателя, создал функцию Then, которая принимает обратный вызов и возвращает Promise, и просто вызывает обратный вызов для функции resolver.

class MyPromise {

    constructor(resolver) {
        this.resolver = resolver;
    }

    then(callback) {
        const result = new MyPromise(callback);
        this.resolver(callback);

        return result;
    }
}

promise = new MyPromise(
    (result) => {
        setTimeout(result(2), 500);
    });
promise.then(result => {
    console.log(result);
    return 2 * result;
}).then(result => console.log(result));

Ожидаемый результат 2,4 - простонравится работать с реальным обещанием.Но я получаю 2,2.У меня проблемы с выяснением, как получить возвращаемое значение для первого «затем» и передать его.

Ответы [ 5 ]

3 голосов
/ 05 апреля 2019

Вот сокращенный код для создания класса обещания,

class MyPromise {
  constructor(executor) {
    this.callbacks = [];

    const resolve = res => {
      for (const { callback } of this.callbacks) {
        callback(res);
      }
    };

    executor(resolve);
  }

  then(callback) {
    return new MyPromise((resolve) => {
      const done = res => {
        resolve(callback(res));
      };
      this.callbacks.push({ callback: done });
    });
  }
}


promise = new MyPromise((resolve) => {
  setTimeout(() => resolve(2), 1000);
});

promise.then(result => {
  console.log(result);
  return 2 * result;
}).then(result => console.log(result));
2 голосов
/ 05 апреля 2019

Как насчет очень простого:

const SimplePromise = function(cb) {
  cb(
    data =>
      (this.data = data) &&
      (this.thenCb || []).forEach(chain => (this.data = chain(this.data))),
    error =>
      (this.error = error) &&
      (this.catchCb || []).forEach(chain => (this.error = chain(this.error)))
  );
  this.then = thenCb =>
    (this.thenCb = [...(this.thenCb || []), thenCb]) && this;
  this.catch = catchCb =>
    (this.catchCb = [...(this.catchCb || []), catchCb]) && this;
};

Пример здесь: https://codesandbox.io/s/0q1qr8mpxn

2 голосов
/ 05 апреля 2019

Ваш вопрос имеет некоторые проблемы:

  • Переменная r2 нигде не определена. Я предполагаю, result был предназначен.
  • setTimeout не делает ничего полезного, так как вы выполняете result(2) немедленно. Я предполагаю, что setTimeout(() => result(2), 500) было задумано.

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

Одна проблема с вашей попыткой состоит в том, что обещание, возвращаемое методом then (т. Е. result), никогда не разрешается. Вам необходимо разрешить его, как только будет выполнено обещание this со значением, возвращаемым обратным вызовом then.

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

В следующем решении сделано несколько упрощений по сравнению с правильным поведением Promise.

  • Не вызывает асинхронные обратные вызовы then;
  • Он не поддерживает несколько вызовов then для одного и того же обещания;
  • Это не обеспечивает путь отклонения;
  • Это не мешает выполнению обещания дважды с другим значением;
  • Это не касается особого случая, когда then обратный вызов возвращает обещание

console.log("Wait for it...");

class MyPromise {
    constructor(executor) {
        executor(result => this.resolve(result));
    }
    resolve(value) {
        this.value = value;
        this.broadcast();
    }
    then(onFulfilled) {
        const promise = new MyPromise(() => null);
        this.onFulfilled = onFulfilled;
        this.resolver = (result) => promise.resolve(result);
        this.broadcast();
        return promise;
    }
    broadcast() {
        if (this.onFulfilled && "value" in this) this.resolver(this.onFulfilled(this.value)); 
    }
};

// Code provided by interviewer, including two corrections
promise = new MyPromise(
    (result) => {
        setTimeout(()=>result(2), 500); // don't execute result(2) immediately
    });
promise.then(result => {
    console.log(result); // Changed r2 to result.
    return 2 * result;
}).then(result => console.log(result));

Обратите внимание, что задержка на выходе составляет 500 мс, что и следует ожидать от (исправленного) кода setTimeout.

Я разместил полную реализацию обещаний, совместимых с Promises / A +, с комментариями в этот ответ

0 голосов
/ 05 апреля 2019

Есть несколько проблем с вашим исходным кодом.Примечательно, что вы выполняете аргумент конструктора только при вызове метода then, и вы на самом деле не объединяете в цепочку результаты обратных вызовов then.

Вот очень (очень!) Базовая реализация обещаний, основанная на адаптации вашего примера.Это также будет работать в случаях, когда 'then' вызывается после того, как обещание разрешено (но не, если 'then' уже вызвано - несколько блоков then не поддерживаются).

class MyPromise {
    constructor(resolver) {
        let thisPromise = this;

        let resolveFn = function(value){
            thisPromise.value = value; 
            thisPromise.resolved = true;
            if(typeof thisPromise.thenResolve === "function"){
                thisPromise.thenResolve();
            }
            
        }
        if (typeof resolver === "function") {
            resolver(resolveFn);
        }
    }
    then(callback) {
        let thisPromise = this;
        thisPromise.thenFn = callback;

        return new MyPromise((resolve) =>{
            thisPromise.thenResolve = () => {
                thisPromise.value = thisPromise.thenFn(thisPromise.value);
                resolve(thisPromise.value);
            }
            //automatically resolve our intermediary promise if 
            //the parent promise is already resolved
            if(thisPromise.resolved){
                thisPromise.thenResolve();
            }
        });

        
    }
};

//test code

console.log("Waiting for Godot...");

promise = new MyPromise((resolve) =>{
    setTimeout(()=>{
        resolve(2)
    },500);
});


promise.then((result) => {
    console.log(result);
    return 2 * result;
}).then((result) => {
    console.log(result);
    return 2 * result;
}).then((result) => {
    console.log(result)
}); 
0 голосов
/ 05 апреля 2019

Подразумевается, что этот r2 фактически является параметром результата.Проблема с вашим кодом в том, что вы не извлекаете результат из результата (2).Первое «затем» выполняется, печатает 2, возвращает 4, но это 4 просто напрасно.Я написал некоторый код с синхронной функцией только для демонстрации того, что делать, если вы хотите получить этот вывод 2,4:

class MyPromise {
    constructor(resolver) {
        this.resolver = resolver;
    }

    then(callback) {
          var res = callback(this.resolver());
        var result = new MyPromise(() => { return res; });

        return result;
    }
}

let promise = new MyPromise(
    () => {
        return 2;
    });

promise
.then(result => {
    console.log(result);
    return 2 * result;
})
.then(result => {
        console.log(result);
});

Если вы хотите, чтобы преобразователь был несколько асинхронным, вы должны использовать Promises (потому что возвращаемое значениеИз функции, выполняемой внутри setTimeout, можно извлечь, см. здесь .

Если вам не разрешено использовать эти встроенные Обещания, вы можете написать их самостоятельно с каким-то фиктивным отложенным объектом и ожидать их(setInterval) для разрешения (должна быть в основном такая же логика).

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