Обещание от записи до почерка | [Обещание, серия 1]

JavaScript

Автор: холодная трава
WeChat: hancao97
Введение: другой программист Beidrift, добро пожаловать, чтобы добавить WeChat для критики и исправления коммуникационных технологий.

письменный фон

Собственно, то, что я хотел написать сначала, было неPromiseЭта статья, но я хотел бы обобщить некоторыеaxiosРелевантные знания, но написание статей должно основываться на них, если вы хотите хорошо говоритьaxios, то вы должны понять его пред-знание, так что я думаю, что мы могли бы также сделать это в серию, так что есть изPromise,Ajaxначать, говоритьAxiosстроить планы. На самом деле я считаю, что все, кто занимается фронтенд-разработкой, должны быть правы.PromiseЯ не чужой, так что вы, ребята, можете следовать за мной.PromiseЭти базовые знания повторяются. Эта статья будет включать:

  • Promiseпредставлять
  • PromiseФункции
  • Promiseиспользовать
  • Согласно использованию и характеристикам предыдущего текста, рукописныйPromise

Без лишних слов, давайте начнем!

Введение в промисы

Обещание происхождения и использования

  • PromiseВпервые он был предложен и реализован в сообществе, а стандарт языка был написан на ES6.

совет: ES6 — это шестая редакция ECMA-262. Эта редакция содержит наиболее важные усовершенствования за всю историю этой спецификации. Javascript — это реализация спецификации ECMA-262.

  • PromiseЭто решение для асинхронного программирования, которое является более разумным, мощным и элегантным, чем традиционное решение с функцией обратного вызова.
  • Синтаксически:Используйте конструктор Promise для инкапсуляции асинхронных операций для создания экземпляров Promise.
  • Функционально:Объект обещания используется для инкапсуляции асинхронной операции и предоставления унифицированного API, чтобы различные асинхронные операции могли обрабатываться одинаково.

Общие сценарии асинхронного программирования

  • файловые операции fs
require('fs').readFile('./index.html',(err,data)=>{
    // 回调函数
})
  • Аякс операции
$.get('/api/getUser',(data)=>{
    //handleData();
})
  • таймер
setTimeout(() => {
    console.log('timeout');
},1000);

Зачем использовать обещания

  • Поддержка цепочек вызовов, выражение асинхронных операций в процессе синхронных операций, что может решить проблему callback hell

Что такое ад обратного вызова?
Функция обратного вызова называется вложенной, а результатом асинхронного выполнения внешней функции обратного вызова является условие выполнения вложенного обратного вызова.

// 回调地狱典型场景
asyncFunc1(opt,(...args1) => {
    asyncFunc2(opt,(...args2) => {
        asyncFunc3(opt,(...args3) => {
            asyncFunc4(opt,(...args4) => {
                //TODO: some opt
            })
        })
    })
})

Недостатки callback hell:
Нелегко читать, нелегко обрабатывать исключения, не способствует физическому и умственному удовольствию.

  • Более гибкий способ указания функций обратного вызова.

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

  • Обещания предоставляют унифицированный API, упрощающий управление асинхронными операциями.

Какие API предоставляются, будет уточнено при последующем использовании.

Особенности обещания

Свойства промисов

  • Состояние объекта не зависит от внешнего мира

PromiseОбъект представляет собой асинхронную операцию и имеет три состояния:pendingв ходе выполнения,fulfilledуспех,rejectedпотерпеть неудачу Состояние можно изменить только после завершения асинхронной операции, и никакие другие операции не могут его изменить. Состояние хранится в объектах Promise[[PromiseState]]в свойствах.

// Promise的两个属性:
let promiseA = new Promise((resolve,reject)=>{resolve();})
// 状态对应promiseA的[[PromiseState]]字段。
let promiseB = new Promise((resolve,reject)=>{resolve(1111);})
// [[PromiseResult]]的值为 1111,这个字段用于存储 resolve(val)或者reject(val)的参数[val]

Совет: обещание должно быть обещанием, и по этой причине подумайте о том, как это романтично.

  • Как только состояние изменится, оно больше не изменится

Есть только две возможности для изменения состояния объекта Promise: отPending 变为 Resolvedи изPending 变为 Rejected. Пока происходят эти две ситуации, состояние заморожено и больше не изменится, и этот результат будет всегда поддерживаться. Даже если изменение уже произошло, если вы добавите callback-функцию к объекту Promise, вы немедленно получите этот результат. Это совсем не то, что событие, характеристика события в том, что если вы пропустите его и послушаете снова, вы не получите результата.

Недостатки обещаний

  • Однажды вновь созданный,выполнить немедленно, вы не можете отменить на полпути
  • PromiseВнутренние ошибки, которые не могут быть отражены извне
  • pendingНевозможно узнать, на какой стадии он продвинулся.

Обещания использования

Создание экземпляра обещания

const promise = new Promise((resolve, reject) => {
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Конструктор Promise принимает в качестве параметра функцию, два параметра которойresolveа такжеreject. Это две функции, предоставляемые движком JavaScript, без их самостоятельного развертывания.

  • разрешить функцию: изменить состояние объекта Promise с «незавершенного» на «успешное» (то есть сpendingсталиfulfilled), вызывается при успешном выполнении асинхронной операции, и результат асинхронной операции передается в качестве параметра;
  • функция отклонения: изменить состояние объекта Promise с «невыполнено» на «не выполнено» (т. е. сpendingсталиrejected), вызываемый при сбое асинхронной операции, и передает сообщение об ошибке асинхронной операции в качестве параметра.

Promise.prototype.then

После создания экземпляра Promise вы можете использоватьthenспособы оговариваются отдельноfulfilledстатус иrejectedфункция обратного вызова состояния
в конечном итоге вернет новыйPromiseобъект

подсказка: функция обратного вызова в then не является обязательной

promise.then(function(value) {
  // fulfilled状态的处理
}, function(error) {
  // rejected状态的处理
});

Promise.prototype.catch

catch используется для обработки состояния какrejectedфункция обратного вызова
в конечном итоге вернет новыйPromiseобъект

promise.catch((err)=>{
    handleReject();
    //
})

Promise.resolve

Возвращает успешный или неудачный объект Promise

let promiseA = Promise.resolve(1);
// 如果传入的参数为非Promise类型的对象,则返回的结果为成功的Promise对象
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
    reject('err');
})
// 如果传入的参数为Promise对象,则参数Promise返回的结果就是 Promise.resolve返回的结果
// 比如这时return 一个[[PromiseResult]]的值为err的Promise对象

Promise.reject

Возвращает неудачный объект Promise

let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
    resolve('err');
})
// 无论传入是啥,就返回一个失败的Promise对象,[[PromiseResult]]的值为 Promise.reject的参数

Promise.all

Полученный аргумент представляет собой массив из n объектов Promise.

подсказки: возвращаемый результат — новое обещание. Оно будет успешным, только если все объекты Promise будут успешными.

let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.all([promise1,promise2,promise3]);
//此时输出为 [[PromiseState]]是rejected,[[PromiseResult]]是3的Promise
const res = Promise.all([promise1,promise2]);
//此时输出为 [[PromiseState]]是fulfilled,[[PromiseResult]]是[1,2]的Promise

Promise.race

Полученный аргумент представляет собой массив из n объектов Promise.

подсказки: возвращаемый результат — это новый промис, а статус результата первого выполненного промиса — это статус конечного результата.

let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.race([promise1,promise2,promise3]);
//此时输出为 [[PromiseState]]是fulfilled,[[PromiseResult]]是1的Promise

Общая проблема

Как изменить состояние объекта Promise

  • resolve() // pending => fulfilled
  • reject() // pending => rejected
  • выбросить ошибку выбросить 'err' // в ожидании => отклонено

Возвращаемый результат promise.then()

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

Почему обещания могут быть связаны?

Потому что возвращаемое значение всех API обещаний, таких как then, catch, all, race и т. д., является новым объектом обещания.
Таким образом, вы можете продолжать вызывать метод обещания и связывать задачи таким образом.

Исключение проникновения промисов

  • При цепочке с обещанием в конце может быть указан неудачный обратный вызов.
  • Любые предыдущие ошибки будут переданы неудачному обратному вызову в конце для обработки.
let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    throw 'err';
}).then((value)=>{
    console.log(22);
}).catch(err=>{
    console.log(err);
})
//输出: 11  err

Разорвите цепочку обещаний

let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    console.log(22);
}).then((value)=>{
    console.log(33);
}).catch(err=>{
    console.log(err);
})
//输出:11 22 33

Так как же нам прервать совместную отладку этой функции обратного вызова?

let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    console.log(22);
    return new Promise(()=>{});
}).then((value)=>{
    console.log(33);
}).catch(err=>{
    console.log(err);
})
//输出:11 22

Ответ состоит в том, чтобы вернуть статусpendingизpromiseобъект

Рукописное обещание

Код повторяется шаг за шагом, вы можете проверить это, я написал комментарии более понятно~

Реализация конструктора

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

tips:

  1. Уведомление! Throw err также может изменить состояние и результат Promise!
  2. Состояние обещания можно изменить только один раз, что необходимо ограничить
// 万里长城第一步
function Promise(executor){
    this.promiseState = 'pending';
    this.promiseResult = null;
    const resolve = val => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
        this.promiseState = 'fulfilled';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = val;
    }

    const reject = err => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
        this.promiseState = 'rejected';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = err;
    }
    // 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
    try{
        /*
        * 同步执行执行器函数
        * 执行器函数接收两个参数,一个是resolve,一个是reject
        */
        executor(resolve,reject);
    } catch(err) {
        reject(err);
    }
}

Реализация метода then

Во-первых, согласно предыдущему, затем поддерживает два параметра, которые являются успешными и неудачными обратными вызовами, и эти два параметра могут передаваться.
Во-вторых, из-за проблемы асинхронных задач и поддержки множественных обратных вызовов нам необходимо хранить функцию обратного вызова в массиве, поэтому вводится новая переменная,callbackList
И мы должны отметить, что возвращаемый результат then такжеPromiseТема [Если не помните, оглянитесь назад~]

// 万里长城今犹在~
function Promise(executor){
    //保存promise状态
    this.promiseState = 'pending';
    //保存promise结果
    this.promiseResult = null;
    //用于保存异步回调函数列表
    this.callbackList = [];
    const resolve = val => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
        this.promiseState = 'fulfilled';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = val;
        // 调用成功的回调【callbackList存起来的】
        for(let callback of this.callbackList){
            callback.onResolved(val);
        }

    }

    const reject = err => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
        this.promiseState = 'rejected';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = err;
        // 调用失败的回调【callbackList存起来的】
        for(let callback of this.callbackList){
            callback.onRejected(err);
        }
    }
    // 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
    try{
        /*
        * 同步执行执行器函数
        * 执行器函数接收两个参数,一个是resolve,一个是reject
        */
        executor(resolve,reject);
    } catch(err) {
        reject(err);
    }
}
//then方法
Promise.prototype.then = function(onResolved,onRejected){
    const self = this;

    // then方法会返回Promise
    return new Promise((resolve,reject) => {
        // 对返回值的处理进行封装
        const handleCallback = (callback) => {
            // 如果回调函数中抛出错误,则reject
            try{
                // 需要依据回调的返回结果确定then方法的返回值
                // 现在的this会指向return的promise对象,所以使用self
                const res = callback(self.promiseResult);
                if(res instanceof Promise){
                    //如果回调返回结果是个Promise
                    res.then(val => {
                        resolve(val);
                    },err => {
                        reject(err);
                    })
                }else{
                    // 返回结果不是Promise
                    resolve(res);
                }
            }catch(err){
                reject(err);
            }
        }
        //调用回调函数
        if(this.promiseState === 'fulfilled'){
            handleCallback(onResolved);
        }
        if(this.promiseState === 'rejected'){
            handleCallback(onRejected);
        }
        /*
        * 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
        * 所以要保存回调函数
        * 因为promise实例阔以指定多个回调,于是采用数组 
        */
        if(this.promiseState === 'pending'){
            this.callbackList.push({
                onResolved:() => {
                    handleCallback(onResolved); 
                },
                onRejected:() => {
                    handleCallback(onRejected);
                }
            })
        }
    })
}

Реализация метода catch

Мы используем метод then для реализации метода catch, но метод catch может обрабатывать проникновение исключений [я говорил ранее~]

// 我想找人陪我去长城玩,哈哈哈哈
function Promise(executor){
    //保存promise状态
    this.promiseState = 'pending';
    //保存promise结果
    this.promiseResult = null;
    //用于保存异步回调函数列表
    this.callbackList = [];
    const resolve = val => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
        this.promiseState = 'fulfilled';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = val;
        // 调用成功的回调【callbackList存起来的】
        for(let callback of this.callbackList){
            callback.onResolved(val);
        }

    }

    const reject = err => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
        this.promiseState = 'rejected';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = err;
        // 调用失败的回调【callbackList存起来的】
        for(let callback of this.callbackList){
            callback.onRejected(err);
        }
    }
    // 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
    try{
        /*
        * 同步执行执行器函数
        * 执行器函数接收两个参数,一个是resolve,一个是reject
        */
        executor(resolve,reject);
    } catch(err) {
        reject(err);
    }
}
//then方法
Promise.prototype.then = function(onResolved,onRejected){
    const self = this;
    //处理异常穿透 并且为onResolved,onRejected设置默认值。因为这两个参数可以都不传
    if(typeof onRejected !== 'function'){
        onRejected = err => {
            throw err;
        }
    }
    if(typeof onResolved !== 'function'){
        onResolved = val => val;
    }
    // then方法会返回Promise
    return new Promise((resolve,reject) => {
        // 对返回值的处理进行封装
        const handleCallback = (callback) => {
            // 如果回调函数中抛出错误,则reject
            try{
                // 需要依据回调的返回结果确定then方法的返回值
                // 现在的this会指向return的promise对象,所以使用self
                const res = callback(self.promiseResult);
                if(res instanceof Promise){
                    //如果回调返回结果是个Promise
                    res.then(val => {
                        resolve(val);
                    },err => {
                        reject(err);
                    })
                }else{
                    // 返回结果不是Promise
                    resolve(res);
                }
            }catch(err){
                reject(err);
            }
        }
        //调用回调函数
        if(this.promiseState === 'fulfilled'){
            handleCallback(onResolved);
        }
        if(this.promiseState === 'rejected'){
            handleCallback(onRejected);
        }
        /*
        * 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
        * 所以要保存回调函数
        * 因为promise实例阔以指定多个回调,于是采用数组 
        */
        if(this.promiseState === 'pending'){
            this.callbackList.push({
                onResolved:() => {
                    handleCallback(onResolved); 
                },
                onRejected:() => {
                    handleCallback(onRejected);
                }
            })
        }
    })
}

//catch方法
Promise.prototype.catch = function(onRejected) {
    //  我们可以直接使用then方法实现
    return this.then(undefined,onRejected);
}

Реализация метода Promise.resolve

Просто, ничего не говори

//resolve方法
Promise.resolve = function(val) {
    //返回值的情况在前文说过,可以在 Promise的使用一章找到
    return new Promise((resolve,reject)=>{
        if(val instanceof Promise){
            val.then(val => {
                resolve(val);
            }, err => {
                reject(err);
            });
        }else{
            resolve(value);
        }
    }) 
}

Реализация метода Promise.reject

Так проще, ничего не говори

 //reject方法
Promise.reject = function(err) {
    //返回值的情况在前文说过,可以在 Promise的使用一章找到
    return new Promise((resolve,reject)=>{
        reject(err);
    }) 
}

Реализация метода Promise.all

Это относительно просто. Оглядываясь назад на предыдущее использование и возвращаемое значение Promise.all, легко понять ~

//可以先去回顾一下all方法的用法
//all
Promise.all = function(promiseList) {
    let count = 0;
    let res = [];
    const length = promiseList.length;
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < length; i++){
            promiseList[i].then(val => {
                count++;
                res[i] = val;
                if(count === length){
                    resolve(res);
                }
            },err => {
                reject(err);
            });
        }
    }) 
}

Реализация метода Promise.race

Это относительно просто. Оглядываясь назад на предыдущее использование и возвращаемое значение Promise.race, легко понять ~

//race
//要结束了!
Promise.race = function(promiseList) {
    const length = promiseList.length;
    //谁先完成谁就决定结果!
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < length; i++){
            promiseList[i].then(val => {
                resolve(val);
            },err => {
                reject(err);
            });
        }
    }) 
}

Полный код и детальная обработка

подсказки: функция обратного вызова деталей является асинхронной, и мы используем setTimeout для ее переноса

function Promise(executor){
    //保存promise状态
    this.promiseState = 'pending';
    //保存promise结果
    this.promiseResult = null;
    //用于保存异步回调函数列表
    this.callbackList = [];
    const resolve = val => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
        this.promiseState = 'fulfilled';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = val;
        setTimeout(() => {
            // 调用成功的回调【callbackList存起来的】
            for(let callback of this.callbackList){
                callback.onResolved(val);
            }
        })
    }

    const reject = err => {
        // 状态只能修改一次
        if(this.promiseState !== 'pending') return;
        // 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
        this.promiseState = 'rejected';
        // 2. 要去修改Promise对象的状态([[promiseResult]])
        this.promiseResult = err;
        setTimeout(() => {
            // 调用失败的回调【callbackList存起来的】
            for(let callback of this.callbackList){
                callback.onRejected(err);
            }
        })
    }
    // 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
    try{
        /*
        * 同步执行执行器函数
        * 执行器函数接收两个参数,一个是resolve,一个是reject
        */
        executor(resolve,reject);
    } catch(err) {
        reject(err);
    }
}
//then方法
Promise.prototype.then = function(onResolved,onRejected){
    const self = this;
    //处理异常穿透 并且为onResolved,onRejected设置默认值。因为这两个参数可以都不传
    if(typeof onRejected !== 'function'){
        onRejected = err => {
            throw err;
        }
    }
    if(typeof onResolved !== 'function'){
        onResolved = val => val;
    }
    // then方法会返回Promise
    return new Promise((resolve,reject) => {
        // 对返回值的处理进行封装
        const handleCallback = (callback) => {
            // 如果回调函数中抛出错误,则reject
            try{
                // 需要依据回调的返回结果确定then方法的返回值
                // 现在的this会指向return的promise对象,所以使用self
                const res = callback(self.promiseResult);
                if(res instanceof Promise){
                    //如果回调返回结果是个Promise
                    res.then(val => {
                        resolve(val);
                    },err => {
                        reject(err);
                    })
                }else{
                    // 返回结果不是Promise
                    resolve(res);
                }
            }catch(err){
                reject(err);
            }
        }
        //调用回调函数
        if(this.promiseState === 'fulfilled'){
            setTimeout(()=>{
                handleCallback(onResolved);
            })
        }
        if(this.promiseState === 'rejected'){
            setTimeout(()=>{
                handleCallback(onRejected);
            })
        }
        /*
        * 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
        * 所以要保存回调函数
        * 因为promise实例阔以指定多个回调,于是采用数组 
        */
        if(this.promiseState === 'pending'){
            this.callbackList.push({
                onResolved:() => {
                    handleCallback(onResolved); 
                },
                onRejected:() => {
                    handleCallback(onRejected);
                }
            })
        }
    })
}
//catch方法
Promise.prototype.catch = function(onRejected) {
    //  我们可以直接使用then方法实现
    return this.then(undefined,onRejected);
}
//resolve方法
Promise.resolve = function(val) {
    //返回值的情况在前文说过,可以在 Promise的使用一章找到
    return new Promise((resolve,reject)=>{
        if(val instanceof Promise){
            val.then(val => {
                resolve(val);
            }, err => {
                reject(err);
            });
        }else{
            resolve(value);
        }
    }) 
}

//reject方法
Promise.reject = function(err) {
    //返回值的情况在前文说过,可以在 Promise的使用一章找到
    return new Promise((resolve,reject)=>{
        reject(err);
    }) 
}

//all
Promise.all = function(promiseList) {
    let count = 0;
    let res = [];
    const length = promiseList.length;
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < length; i++){
            promiseList[i].then(val => {
                count++;
                res[i] = val;
                if(count === length){
                    resolve(res);
                }
            },err => {
                reject(err);
            });
        }
    }) 
}

//race
Promise.race = function(promiseList) {
    const length = promiseList.length;
    //谁先完成谁就决定结果!
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < length; i++){
            promiseList[i].then(val => {
                resolve(val);
            },err => {
                reject(err);
            });
        }
    }) 
}

На этом глава нашего рукописного Обещания окончена~

заключительные замечания

Советы: На самом деле, я очень хочу пожаловаться на себя, почему я всегда говорю куриная кровь или куриный суп в конце блога, ха-ха

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