Полная коллекция асинхронных решений программирования, генератор + CO, ASYNC + a ждать

Promise

Введение

Традиционным решением для однопоточного выполнения кода является回调函数和事件. Это решение проблемы, но создает ад обратного вызова.

Асинхронное программирование — это ключ к оптимизации логики кода для улучшения его читаемости.

В настоящее время существует три распространенных метода асинхронного программирования:

  1. Promise
  2. generator+co
  3. async+await

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

2. Обещания

2.1 Принцип

Объект Promise — это конструктор, который генерирует экземпляры Promise.

Объект Promise представляет собой асинхронную операцию и имеет три состояния:pending(в ходе выполнения),fulfilled(успешно) иrejected(не удалось).

Два параметра являются функциями Promiseresolveа такжеreject. Это две функции, определенные в промисе, которые возвращаются при запуске пользовательской функции.

resolveФункция преобразует состояние объекта Promise изpendingсталиresolved,rejectИзмените состояние объекта Promise сpendingсталиrejected

Метод then определен в цепочке прототипов Promise, и предусмотрены две функции обратного вызова для захвата значений, возвращаемых resolve и reject соответственно.

2.2 Статические методы
метод описывать
Promise.resolve(promise); вернуть обещание (только если promise.constructor == Promise)
Promise.resolve(thenable); Создайте новое обещание из файла thenable. то можно иметьthen()Объект, похожий на промис, с методами.
Promise.resolve(obj); В этом случае сгенерируйте обещание и верните obj при выполнении.
Promise.reject(obj); Дает обещание и возвращает obj в случае отказа. В целях согласованности и отладки (например, для трассировки стека) obj должен иметь значение instanceof Error.
Promise.all(array); Создает обещание, которое выполняется, когда выполняется каждый элемент в массиве, и отклоняется, если какой-либо из них отклонен.
Promise.race(array); Создает обещание, которое выполняется, когда любой элемент выполнен, или отклоняется, когда любой элемент отклоняется, в зависимости от того, что произойдет раньше.

sample 1

let p1 = new Promise((resolve,reject)=>{
    console.log('hello')
    setTimeout(function () {
        reject('1212')
    },1000)
})

p1.then(data=> {
    console.log('success'+data)
},err=>{
    console.log('err'+err)
})

p1.then(data=> {
    console.log('success'+data)
},err=>{
    console.log('err'+err)
})

terminal:

hello
err1212
err1212

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

Вызов метода then в примере 1 не приводит к немедленному выполнению обратного вызова. Это ожидание изменения состояния экземпляра перед выполнением. это и发布订阅Узоры похожи.

sample 2

let fs = require('fs')

let event = {
    arr:[],
    result:[],
    on(fn){
        this.arr.push(fn)
    },
    emit(data){
        this.result.push(data)
        this.arr.forEach(fn=>fn(this.result))
    }
}

event.on(function (data) {
    if(data.length === 2){
        console.log(data)
    }
})

fs.readFile('1.txt','utf8',function (err,data) {
    event.emit(data)
})
fs.readFile('2.txt','utf8',function (err,data) {
    event.emit(data)
})

В smaple2 данные результата помещаются во временный массив хранения и возвращаются при выполнении отвечающей функции.

2.3 Сокращение исходного кода Promise

Благодаря предыдущим примерам и пониманию модели публикации-подписки мы можем примерно написать основные функции экземпляра Promise:

код 1:

function Promise(executor) {
    let self = this
    self.value = undefined
    self.reason = undefined
    self.status = 'pending'
    self.onResovedCallbacks = []
    self.onRejectedCallbacks = []
    function resolve(data) {
        if(self.status === 'pending'){
            self.value = data
            self.status = 'resolved'
            self.onResovedCallbacks.forEach(fn=>fn())
        }
    }
    function reject(reason) {
        if(self.status === 'pending') {
            self.reason = reason
            self.status = 'reject'
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }
    //如果函数执行时发生异常
    try{
        executor(resolve,reject)
    }catch (e){
        reject(e)
    }
}

Promise.prototype.then = function (onFulfilled,onRejected) {
    let self = this
    if(self.status === 'pending'){
        self.onResovedCallbacks.push(()=>{
            onFulfilled(self.value)
        })
        self.onRejectedCallbacks.push(()=>{
            onRejected(self.reason)
        })
    }else if(self.status === 'resolved'){
        onFulfilled(self.value)
    }else if(self.status === 'reject'){
        onRejected(self.reason)
    }

}

module.exports = Promise
  • переменная внутри функции
    • статус: сохранить статус промиса
    • onResovedCallbacks: сохранить успешную функцию обратного вызова в состоянии ожидания обещания.
    • onRejectedCallbacks: сохраняет функцию обратного вызова в случае сбоя в состоянии ожидания обещания.
    • Разрешить функцию
    • функция отклонения
  • Promise.prototype.then
    • Выполняет обратный вызов, который отвечает на состояние экземпляра
    • status == pending Используйте режим публикации-подписки для хранения функций обратного вызова.
2.4 Краткое описание использования Promise
  1. Если обещание выполняется и возвращается объект обещания, результат выполнения обещания будет передан следующему затем.
let fs = require('fs')

function read(filePath,encoding) {
    return new Promise((resolve,reject)=>{
        fs.readFile(filePath,encoding,(err,data)=> {
            if(err) reject(err)
            resolve(data)
        })
    })
}

read('1.txt','utf8').then(
    f1=>read(f1,'utf8') // 1
).then(
    data=> console.log('resolved:',comments)
    err=> console.log('rejected: ',err)
)
  1. Если возвращаемое значение then является не обещанием, а обычным значением, это обычное значение будет использоваться в качестве возвращаемого результата следующего then.
 ......
 
read('1.txt','utf8').then(
    f1=>read(f1,'utf8')
).then(
    return 123 //2
).then(
    data=> console.log('resolved:',comments)
    err=> console.log('rejected: ',err)
)
  1. Если текущий затем выходит из строя, следующий затем терпит неудачу.
 ......
 
read('1.txt','utf8').then(
    f1=>read(f1,'utf8')
).then(
    return 123
).then(
    throw new Error('出错') //3
).then(
    data=> console.log('resolved:',comments)
    err=> console.log('rejected: ',err)
)
  1. Если возвращается undefined, он перейдет к следующему успеху независимо от того, является ли он в настоящее время неудачным или успешным.
  2. catch — это случай, когда ошибка не обрабатывается.
  3. Тогда можно не писать.
 ......
 
read('1.txt','utf8').then(
    f1=>read(f1,'utf8')
).then(
    return 123
).then(
    throw new Error('出错') 
).then() //6
 .then(
    data=> console.log('resolved:',comments)
    err=> console.log('rejected: ',err)
)

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

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

код 2:

function Promise(executor) {
    let self = this
    self.value = undefined
    self.reason = undefined
    self.status = 'pending'
    self.onResovedCallbacks = []
    self.onRejectedCallbacks = []
    function resolve(value) {
        if (self.status === 'pending') {
            self.value = value
            self.status = 'resolved'
            self.onResovedCallbacks.forEach(fn=>fn())
        }
    }

    function reject(reason) {
        if (self.status === 'pending') {
            self.reason = reason
            self.status = 'rejected'
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }

    //如果函数执行时发生异常
    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}


function resolvePromise(promise2, x, resolve, reject) {
    //If promise and x refer to the same object, reject promise with a TypeError as the reason.
    if (promise2 === x) {
        return reject(new TypeError('chaining cycle'))
    }
    let called
    //2.3.3.Otherwise, if x is an object or function,
    if (x !== null && (typeof x == 'object' || typeof x === 'function')) {
        try {
            let then = x.then
            //2.3.3.3.If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:
            //2.3.3.3.3.If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
            if (typeof then === 'function') {
                then.call(x, y=> {
                    if (called) return;
                    called = true;
                    //递归直到解析成普通值为止
                    //2.3.3.1.If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
                    resolvePromise(promise2, y, resolve, reject)
                }, err=> {
                    if (called) return;
                    called = true;
                    reject(err)
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return;
            called = true;
            //2.3.3.3.If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
            reject(e)
        }
    } else {
        //If x is not an object or function, fulfill promise with x.
        resolve(x)
    }
}
//then调用的时候 都是异步调用 (原生的then的成功或者失败 是一个微任务)
Promise.prototype.then = function (onFulfilled, onRejected) {
    //成功和失败的函数 是可选参数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val=>val;
    onRejected = typeof onRejected === 'function' ? onRejected : (e)=> {throw e};
    let self = this
    let promise2;
    promise2 = new Promise((resolve, reject)=> {
        if (self.status === 'resolved') {
            setTimeout(()=> {
                try {
                    let x = onFulfilled(self.value)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            }, 0)
        } else if (self.status === 'rejected') {
            setTimeout(()=> {
                try {
                    let x = onRejected(self.reason)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (e) {
                    reject(e)
                }
            }, 0)
        } else if (self.status === 'pending') {
            self.onResovedCallbacks.push(()=> {
                setTimeout(()=> {
                    try {
                        let x = onFulfilled(self.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        //当执行成功回调的时候,可能会出现异常,那就用这个异常作为promise2的错误结果
                        reject(e)
                    }
                }, 0)
            })
            self.onRejectedCallbacks.push(()=> {
                setTimeout(()=> {
                    try {
                        let x = onRejected(self.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            })
        }
    })
    return promise2
}
//setTimeout (规范要求)

Promise.reject = function (reason) {
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}

Promise.resolve = function (value) {
    return new Promise((resolve,reject)=>{
        resolve(value)
    })
}

Promise.prototype.catch = function (onReject) {
    return this.then(null,onReject)
}

Promise.defer = Promise.deferred = function () {
    let dfd = {}
    dfd.promise = new Promise((resolve, reject)=> {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd;
}

module.exports = Promise
  1. Чтобы поддерживать цепочку вызовов then, в Promise.then.prototype возвращается новый объект Promise.
return p2 = new Promise()

2. Добавьте метод resolvePromise для обработки результата x функции обратного вызова старого обещания и вызовите методы разрешения/отклонения нового объекта обещания в соответствии с типом x.

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

Три, синяя птица

1: Основное использование метода fs.readFile в NodeJS

const fs = require('fs'),path = require('path');
 
fs.readFile(path.join(__dirname, '1.txt'), 'utf-8', (err, data) => {
 if (err) {
   console.error(err);
 } else {
   console.log(data);
 }
});

2: Используйте инкапсуляцию промисов

let fs = require('fs')

function read(filePath, encoding) {
    return new Promise((resolve, reject)=> {
        fs.readFile(filePath, encoding, (err, data)=> {
            if (err) reject(err)
            resolve(data)
        })
    })
}

read('1.txt', 'utf8').then( data=> data)

API-интерфейс Promise можно использовать, инкапсулировав метод fs.readFile с помощью Promise. Но упаковывать вручную каждый раз сложнее, bluebird может помочь нам упростить этот шаг.

3: В среде NodeJS вы можете начать использовать объект Promise, предоставляемый Bluebird, с помощью const bluebird = require('bluebird') .

Promise.promisify преобразует один метод в объект Promise.

const bluebird = require('bluebird') 
let read = bluebird.promisify(fs.readFile)
read('1.txt', 'utf-8').then(data=> {
    console.log('data promisify', data)
})

использоватьbluebird.promisifyМетод, вы можете упаковать fs.readfile в объект обещания, что очень просто, верните новое обещание Является ли его ядром:

function promisify(fn) {
    return function () {
        return new Promise((resolve, reject)=> {
            fn(...arguments, function (err, data) {
                if (err) reject(err)
                resolve(data)
            })
        })
    }
}

4. Используйте Promise.promisifyAll для автоматического преобразования всех методов объекта в использование Promise.

const bluebird = require('bluebird'),
  fs = require('fs'),
  path = require('path');
Promise.promisifyAll(fs);
 
fs.readFileAsync(path.join(__dirname, 'sample.txt'), 'utf-8')
  .then(data => console.log(data))
  .catch(err => console.error(err));

Суть promisifyAll заключается в обходе объекта, генерации имени некоторых вновь созданных методов и добавлении суффикса «Async» к имени существующего метода.

function promisifyAll(obj) {
    Object.keys(obj).forEach(key=>{
        if(typeof obj[key] === 'function'){
            obj[key+'Async'] = promisify(obj[key])
        }
    })
}

4. генератор+ко

4.1 Введение

Самая большая особенность функции генератора заключается в том, что ее можно использоватьyieldПриостановить выполнение, чтобы отличить обычные функции, добавить знак * перед именем функции.

function *say() {
    let a = yield "test1"
    let b = yield "test2"
}

let it = say();

console.log(1, it.next()) //1 { value: 'test1', done: false }
console.log(2, it.next()) //2 { value: 'test2', done: false }
console.log(3, it.next()) //3 { value: undefined, done: true }

Выполнение метода say() возвращает объект-указатель и не возвращает результат выполнения функции. это итератор итератор

Вам нужно вызвать метод next() объекта указателя, чтобы указатель функции продолжал двигаться и возвращать объект. ({значение:ххх,готово:ххх})

value — это значение после yield, а done указывает, завершена ли функция.

Мы можем использовать функцию генератора для получения вывода результата, но она также должна поддерживать ввод.

Порядок, в котором запускаются функции генератора, следующий:

Используйте it.next() для выполнения функции, и результат не будет возвращен в определенную переменную a. Следующий метод может принимать параметры для ввода данных в тело функции Генератора. Когда передается второй параметр next, его может получить переменная a.

терминал возвращает:

1 { value: 'test1', done: false }
aaa
2 { value: 'test2', done: false }
bbb
3 { value: undefined, done: true }

4.2 Использование

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

let bluebird = require('bluebird')
let fs = require('fs')
let read = bluebird.promisify(fs.readFile)

function *r() {
    let r1 = yield read('1.txt', 'utf-8')
    console.log('r1',r1); // r1 2.txt
    let r2 = yield read(r1, 'utf-8')
    console.log('r2',r2); // r2 3.txt
    let r3 = yield read(r2, 'utf-8')
    console.log('r3',r3); // r3 hello
    return r3
}

Возьмем пример чтения файла: используйте bluebird, чтобы превратить fs.readFile в объект обещания, и передайте содержимое прочитанного файла в качестве входного параметра следующей выполняемой функции.

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

const it_r = r()
it_r.next().value.then(d1=>{
    return it_r.next(d1).value
}).then(d2=>{
    return it_r.next(d2).value
}).then(d3=>{
    return it_r.next(d3).value
}).then(data=>{
    console.log(data) // hello
})

it.next().value возвращает обещание, используйте метод then для получения значения его успешного обратного вызова и переходите к следующему следующему.

Таким образом, мы можем успешно получить желаемое значение, но это слишком хлопотно. Так что есть комбинация генератор+со!

Установить:

$ npm install co

использовать:

co(r()).then(data=> {
    console.log(data)
})

co будет итеративно выполнять метод it.next() до тех пор, пока логическое значение done не станет истинным, и вернет результат функции-генератора.

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

function co(it) {
    return new Promise((resolve, reject)=> {
        function next(data) {
            let {value, done} = it.next(data)
            if(done){
                resolve(value)
            }else{
                value.then(data=> {
                    next(data)
                },reject)
            }
        }
        next()
    })
}

5. асинхронно+ожидание

Асинхронные функции — это синтаксический сахар для функций-генераторов.

Легче в использовании, чем функция генератора

  1. Может сделать код синхронным
  2. можно попробовать+поймать
  3. можно использовать промис апи
async function r() {
   try{
        let r1 = await read('1.txt','utf8')
        let r2 = await read(r1,'utf8')
        let r3 = await read(r2,'utf8')
        return r3
    }catch(e){
        console.log('e',e)
    }
}

r().then(data=> {
    console.log(data)
},err=>{
    console.log('err',err)
})

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

Ссылаться на

](Уууу. Руан Ифэн.com/blog/2015/0…)

Объявление о вакансии

ByteDance набирает сотрудников!

Описание вакансии: Front-end development (senior) ToB направление - видео облако (База: Шанхай, Пекин)

1. Отвечает за производство мультимедийных услуг, таких как аудио и видео по запросу / прямая трансляция / общение в реальном времени и создание облачных платформ для бизнеса;

2. Отвечает за мультимедийную систему качества, эксплуатацию и техническое обслуживание, построение и развитие системы;

3. Хорошо разбирается в абстрактном дизайне, инженерном мышлении, фокусируется на взаимодействии и создает максимальный пользовательский опыт.

Профессиональные требования

1. Компьютеры, связь и электронная информатика и другие смежные специальности являются предпочтительными;

2. Владение различными фронтенд-технологиями, включая HTML/CSS/JavaScript/Node.js и т. д.;

3. Глубокое понимание языка JavaScript с использованием основных сред разработки, таких как React или Vue.js;

4. Приветствуется знание Node.js, понимание Express/KOA и других фреймворков, а также опыт разработки крупномасштабных серверных программ;

5. Иметь определенное представление о пользовательском опыте, интерактивной работе и анализе потребностей пользователей, а также иметь опыт разработки продуктов или интерфейсов;

6. Предпочтение отдается участникам со своими собственными техническими продуктами, работами с открытым исходным кодом или активным сообществом с открытым исходным кодом.

Основные моменты работы

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

У команды есть стандартизированный процесс итерации проекта и идеальная конфигурация ролей проекта; сильная техническая атмосфера, охватывает сообщество открытого исходного кода и регулярно делится, так что каждый может быстро расти вместе с бизнесом и использовать технологии, чтобы изменить мир!

способ доставки

Резюме можно отправлять напрямую по адресу:yuanyuan.wallace@bytedance.com

Вы также можете отсканировать внутренний QR-код для доставки онлайн, с нетерпением жду вашего присоединения! ~