Простейшая реализация Promise, поддерживающая асинхронные цепные вызовы (20 строк)

JavaScript опрос

предисловие

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

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

Реализация этого промиса не рассматривает никаких исключений, а рассматривает только кратчайший код, чтобы читатели могли понять основной принцип асинхронного цепного вызова.

код

Сначала дайте код, это действительно 20 строк.

function Promise(fn) {
  this.cbs = [];

  const resolve = (value) => {
    setTimeout(() => {
      this.data = value;
      this.cbs.forEach((cb) => cb(value));
    });
  }

  fn(resolve);
}

Promise.prototype.then = function (onResolved) {
  return new Promise((resolve) => {
    this.cbs.push(() => {
      const res = onResolved(this.data);
      if (res instanceof Promise) {
        res.then(resolve);
      } else {
        resolve(res);
      }
    });
  });
};

Основной случай

new Promise((resolve) => {
  setTimeout(() => {
    resolve(1);
  }, 500);
})
  .then((res) => {
    console.log(res);
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(2);
      }, 500);
    });
  })
  .then(console.log);

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

  1. Выход 1 через 500 мс
  2. Выход 2 через 500 мс

выполнить

Конструктор

Сначала давайте реализуем конструктор Promise

function Promise(fn) {
  // Promise resolve时的回调函数集
  this.cbs = [];

  // 传递给Promise处理函数的resolve
  // 这里直接往实例上挂个data
  // 然后把onResolvedCallback数组里的函数依次执行一遍就可以
  const resolve = (value) => {
    // 注意promise的then函数需要异步执行
    setTimeout(() => {
      this.data = value;
      this.cbs.forEach((cb) => cb(value));
    });
  }

  // 执行用户传入的函数 
  // 并且把resolve方法交给用户执行
  fn(resolve);
}

Ладно, вернемся и посмотрим на дело после того, как написали сюда

const fn = (resolve) => {
  setTimeout(() => {
    resolve(1);
  }, 500);
};

new Promise(fn);

отдельно,fnЭто функция, переданная пользователем, которая вызывается внутри.resolveПосле функции будетpromiseна экземпляреcbsСделайте все это один раз.

Пока мы не знаемcbsОткуда взялись функции в этом массиве, тогда читайте дальше.

then

Вот самая важная тогда реализация, от которой зависит цепочка:

Promise.prototype.then = function (onResolved) {
  // 这里叫做promise2
  return new Promise((resolve) => {
    this.cbs.push(() => {
      const res = onResolved(this.data);
      if (res instanceof Promise) {
        // resolve的权力被交给了user promise
        res.then(resolve);
      } else {
        // 如果是普通值 就直接resolve
        // 依次执行cbs里的函数 并且把值传递给cbs
        resolve(res);
      }
    });
  });
};

вернуться к делу

const fn = (resolve) => {
  setTimeout(() => {
    resolve(1);
  }, 500);
};

const promise1 = new Promise(fn);

promise1.then((res) => {
  console.log(res);
  // user promise
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(2);
    }, 500);
  });
});

Обратите внимание на название здесь:

  1. мы кладемnew PromiseВозвращенный экземпляр называетсяpromise1

  2. существуетPromise.prototype.thenВ реализации мы создаем новый возврат обещания, назовем егоpromise2

  3. Вызывается пользователемthenметод, пользователь вручную создает обещание и возвращает его для выполнения асинхронных операций, вызывая егоuser promise

затем вthenВ реализации внутреннее this фактически указывает наpromise1

а такжеpromise2входящийfnфункция выполняетthis.cbs.push(), на самом деле кpromise1изcbsФункция помещается в массив, ожидая последующего выполнения.

Promise.prototype.then = function (onResolved) {
  // 这里叫做promise2
  return new Promise((resolve) => {
    // 这里的this其实是promise1
    this.cbs.push(() => {});
  });
};

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

// promise2
return new Promise((resolve) => {
  this.cbs.push(() => {
    // onResolved就对应then传入的函数
    const res = onResolved(this.data)
    // 例子中的情况 用户自己返回了一个user promise
    if (res instanceof Promise) {
      // user promise的情况
      // 用户会自己决定何时resolve promise2
      // 只有promise2被resolve以后
      // then下面的链式调用函数才会继续执行
      res.then(resolve)
    } else {
      resolve(res)
    }
  })
})

Если метод onResolved, переданный пользователем, затем возвращаетuser promise, то этоuser promiseкуда пользователь пойдет сам в нужный моментresolve promise2, то дальше сюдаres.then(resolve)разрешение in будет выполнено:

if (res instanceof Promise) {
    res.then(resolve)
}

Рассмотрим следующий пример:

new Promise((resolve) => {
  setTimeout(() => {
    // resolve1
    resolve(1);
  }, 500);
})
  // then1
  .then((res) => {
    console.log(res);
    // user promise
    return new Promise((resolve) => {
      setTimeout(() => {
        // resolve2
        resolve(2);
      }, 500);
    });
  })
  // then2
  .then(console.log);

then1Весь этот блок фактически возвращаетpromise2,Такthen2На самом деле, это по существуpromise2.then(console.log),

то естьthen2Зарегистрированная функция обратного вызова фактически входит вpromise2изcbsВ массиве обратного вызова, и поскольку мы только что знали,resolve2После звонка,user promiseБудет вызван разрешениемpromise2разрешиться, а затемpromise2внутреннийcbsМассивы запускаются последовательно.

Это позволяет пользователям писать собственныеresolve2После казни,then2Логика будет продолжать выполняться, то естьАсинхронные связанные вызовы.

Краткое содержание статьи

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

Спецификация обещания A+ по-прежнему достойна прочтения каждым квалифицированным фронтенд-разработчиком.

Надеюсь, эта статья поможет вам!