Избранные обещания для интервью

внешний интерфейс JavaScript Promise jQuery опрос

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

Интересно, задумывались ли читатели о том, почему так много интервьюеров любят спрашивать Promise? Можешь подумать~

Общие вопросы об обещании интервью

Давайте рассмотрим некоторые распространенные вопросы интервью для Promises, от поверхностных до глубоких.

  • 1. Вы понимаете промисы?
  • 2, что такое боли, решается обещанием?
  • 3. Существуют ли другие способы решить болевые точки, которые решает Promise? Если да, пожалуйста, перечислите их.
  • 4. Как использовать промисы?
  • 5. Каковы наиболее часто используемые методы Promise? Что они делают?
  • 6. Каков процесс выполнения Promise в цикле событий?
  • 7. Каковы отраслевые реализации Promises?
  • 8. Можете ли вы написать обещание Polyfill вручную?

Если вы можете провести эти вопросы, то интервьюер в основном признает вас. С учетом вышеуказанных вопросов давайте посмотрим вниз.

Причины обещаний

До промисов мы обрабатывали асинхронный сетевой запрос следующим образом:


// 请求 代表 一个异步网络调用。
// 请求结果 代表网络请求的响应。
请求1(function(请求结果1){
    处理请求结果1
})

Это выглядит хорошо.
Однако требования изменились, нам нужно выполнить второй сетевой запрос по результату первого сетевого запроса, код примерно такой:

请求1(function(请求结果1){
    请求2(function(请求结果2){
        处理请求结果2
    })
})

Тоже не выглядит сложным.
но. . Спрос бесконечен, поэтому появился следующий код:

请求1(function(请求结果1){
    请求2(function(请求结果2){
        请求3(function(请求结果3){
            请求4(function(请求结果4){
                请求5(function(请求结果5){
                    请求6(function(请求结果3){
                        ...
                    })
                })
            })
        })
    })
})

Я был ошеломлен на этот раз. . . пресловутыйад обратного звонкаПоявился.

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

Негативные последствия ада обратного вызова следующие:

  • Код раздут.
  • Плохая читабельность.
  • Муфта слишком высокая, а ремонтопригодность плохая.
  • Плохая возможность повторного использования кода.
  • Разводить жуков легко.
  • Исключения могут обрабатываться только в обратных вызовах.

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

let 请求结果1 = 请求1();
let 请求结果2 = 请求2(请求结果1); 
let 请求结果3 = 请求3(请求结果2); 
let 请求结果4 = 请求2(请求结果3); 
let 请求结果5 = 请求3(请求结果4); 

Подобно приведенному выше синхронному письму. Так родилась спецификация Promise, и в отрасли существует множество реализаций для решения болевой точки ада обратных вызовов. такие как знаменитыйQа такжеbluebird,bluebirdОн даже претендует на звание самой быстрой библиотеки классов.

Когда чиновники увидели это, у них есть ответы на вопросы 2 и 7 выше? ^_^

Что такое обещание

Promise — это решение для асинхронного программирования, более разумное и мощное, чем традиционные асинхронные решения [функция обратного вызова] и [событие]. Теперь он включен в спецификацию ES6.

сравнение написания кода

Или используйте приведенный выше пример сетевого запроса, давайте посмотрим на общее написание промиса:

new Promise(请求1)
    .then(请求2(请求结果1))
    .then(请求3(请求结果2))
    .then(请求4(请求结果3))
    .then(请求5(请求结果4))
    .catch(处理异常(异常信息))

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

API

Общий API Promise выглядит следующим образом:

  • Promise.resolve(value)

Метод класса, который возвращает объект Promise, разрешенный со значением 1. Если значение является thenable (то есть с методом then), возвращенный объект Promise будет «следовать» за thenable объектом, принимая его конечное состояние (называемое разрешенным/отклоненным/ожидающим/установленным).
2. Если входящее значение само по себе является объектом Promise, этот объект возвращается как возвращаемое значение метода Promise.resolve.
3. В других случаях вернуть объект Promise с этим значением в качестве статуса успеха.

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

//如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。  
function fn(resolve){
    setTimeout(function(){
        resolve(123);
    },3000);
}
let p0 = new Promise(fn);
let p1 = Promise.resolve(p0);
// 返回为true,返回的 Promise 即是 入参的 Promise 对象。
console.log(p0 === p1);

Передайте объект, который можно использовать, и верните объект Promise, следующий за окончательным состоянием объекта, который можно использовать.

Промисы ES6 относятся к концепции thenable, просто это очень похожее ПРОМИСС. Самый простой пример — это jQuery.ajax, и его возвращаемое значение — это допустимый объект. Но до тех пор, пока метод THEN можно использовать как объект обещания.

//如果传入的 value 本身就是 thenable 对象,返回的 promise 对象会跟随 thenable 对象的状态。
let promise = Promise.resolve($.ajax('/test/test.json'));// => promise对象
promise.then(function(value){
   console.log(value);
});

Возвращает объект Promise, состояние которого было разрешено.

let p1 = Promise.resolve(123); 
//打印p1 可以看到p1是一个状态置为resolved的Promise对象
console.log(p1)
  • Promise.reject

Class, и единственное отличие от resolve состоит в том, что состояние возвращаемого объекта обещания отклонено.

  • Promise.prototype.then

Метод экземпляра, зарегистрируйте функцию обратного вызова для Promise, форма функции: fn(vlaue){}, значение — это возвращаемый результат предыдущей задачи, затем функция должна возвращать результат или новый объект Promise, чтобы разрешить последующее выполнение. затем обратный вызов для получения.

  • Promise.prototype.catch

Метод экземпляра, исключение catch, форма функции: fn(err){}, err — это информация об исключении, выдаваемая обратным вызовом до регистрации catch.

  • Promise.race

Метод класса, который выполняет несколько задач Promise одновременно и возвращает результат задачи Promise, которая была выполнена первой, независимо от того, был ли результат Promise успешным или неудачным. .

  • Promise.all

Class, несколько задач Promise выполняются одновременно.
Если все выполнены успешно, вернуть результаты выполнения всех задач Promise в виде массива. Если задача Promise отклонена, возвращается только результат отклоненной задачи.

  • ...

Вышеупомянутые API-интерфейсы промисов Если мы освоим их, мы сможем умело использовать промисы.

Обязательно больше тренируйтесь и мастерски осваивайте, иначе полупонятое понимание будет натянуто во время собеседования.

Как понимать обещания

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

Мы можем сравнить Promise с няней, набором домашних дел, нужно только ему сказать, он может сделать это за вас, а вы можете сделать другие вещи.
Например, как глава семьи, я должен однажды пойти по делам, но я также должен купить овощи и приготовить их, чтобы отправить в отряд моей жены (пожалуйста, поймите мой статус дома..)

Ходить по делам очень важно, покупки и приготовление пищи также важны. . Но я могу сделать только одну вещь сам.

В это время я могу оставить покупку продуктов и приготовление пищи няне, а ей скажу:

  • Сначала вы идете в супермаркет, чтобы купить продукты.
  • Готовьте из овощей, купленных в супермаркете.
  • Отправьте приготовленные блюда в отделение жены.
  • Позвоните мне после доставки.

Мы знаем, что указанные выше три шага требуют много времени, и мы можем понимать их как три асинхронных задачи. Напишите эту операцию, используя нотацию Promise:

function 买菜(resolve,reject) {
    setTimeout(function(){
        resolve(['西红柿'、'鸡蛋'、'油菜']);
    },3000)
}
function 做饭(resolve, reject){
    setTimeout(function(){
        //对做好的饭进行下一步处理。
        resolve ({
            主食: '米饭',
            菜: ['西红柿炒鸡蛋'、'清炒油菜']
        })
    },3000) 
}
function 送饭(resolve,reject){
    //对送饭的结果进行下一步处理
    resolve('老婆的么么哒');
}
function 电话通知我(){
    //电话通知我后的下一步处理
    给保姆加100块钱奖金;
}

Что ж, теперь, когда я рассортировала четыре задачи, мне нужно сказать няне, чтобы она следовала этому списку задач. Этот процесс важен, потому что няня не знает, что эти вещи нужно делать, не сказав няне. . (Моя няня ленивая)

// 告诉保姆帮我做几件连贯的事情,先去超市买菜
new Promise(买菜)
//用买好的菜做饭
.then((买好的菜)=>{
    return new Promise(做饭);
})
//把做好的饭送到老婆公司
.then((做好的饭)=>{
    return new Promise(送饭);
})
//送完饭后打电话通知我
.then((送饭结果)=>{
    电话通知我();
})

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

Пожалуйста, имейте в виду: если наша последующая задача является асинхронной задачей, она должна возвращать новый объект обещания.
Если последующая задача является синхронной задачей, просто верните результат.
В примере, который мы привели выше, кромепозвони мнеЭто синхронная задача, остальные — асинхронные, а асинхронные задачи возвращают обещанный объект.

В дополнение к этому важно помнить, что объект Promise имеет три состояния, и после изменения состояния его нельзя изменить ни на какое другое состояние.

  • pending, асинхронная задача выполняется.
  • решена (также называемая выполненной), асинхронная задача выполнена успешно.
  • отклонено, асинхронное выполнение задачи не выполнено.

Краткое изложение использования промисов.

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

  • Сначала инициализируйте объект Promise, который можно создать двумя способами: В любом случае будет возвращен объект Promise.

    • 1. новое обещание (fn)
    • 2. Обещание.решить(fn)
  • Затем вызовите метод then объекта обещания, возвращенного на предыдущем шаге, чтобы зарегистрировать функцию обратного вызова.

    • В этом случае функция обратного вызова может иметь один параметр или не иметь никакого параметра. Если функция обратного вызова затем зависит от возвращаемого результата предыдущего шага, она должна принимать параметры. Например
        new Promise(fn)
        .then(fn1(value){
            //处理value
        })
    
  • Наконец, зарегистрируйте обработчик исключений catch для обработки исключений, которые могут быть вызваны предыдущим обратным вызовом.

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

Увидев это, мы можем ответить на вопросы 4 и 5 выше.

Обещания и цикл событий

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

По поводу Promise, в цикле событий тоже есть понятие микрозадачи (microtask) Если вам интересно, можете прочитать мою статью о цикле времени nodejs.Анализ цикла событий nodejs, хоть и несколько отличается со стороны браузера, время выполнения микрозадач Promise не сильно отличается.

Обещайте обновление

В ES6 появился генератор и асинхронный/ожидающий синтаксис, что делает асинхронную обработку ближе к синхронному написанию кода, улучшает читаемость, и в то же время захват исключений и синхронное написание кода имеют тенденцию быть согласованными. Приведенный выше пример можно записать так:

(async ()=>{
    let 蔬菜 = await 买菜();
    let 饭菜 = await 做饭(蔬菜);
    let 送饭结果 = await 送饭(饭菜);
    let 通知结果 = await 通知我(送饭结果);
})();

Это яснее? Следует помнить, что async/await также реализован на основе промисов, поэтому нам по-прежнему необходимо глубоко понимать использование промисов.

Эпилог

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

Вышеупомянутое содержание надеется, что читатели будут больше практиковаться, понимать использование и принцип Promise и сделают интервью более спокойным.