Руководство ECMAScript Promise, Async Await

1- Что такое Promise?

В программировании компьютера, вы часто работаете с функциями (function), и при вызове функции вы почти сразу получаете результат.
function sum(a, b)  {
  return a + b;
}

// Call function:
let result = sum(10, 5);

console.log(result);
 
Например вы создаете функцию с названием  downloadFile(url), чтобы скачать файл с  Internet.
Скачивание тяжелого файла может занять несколько минут или дольше. При вызова функци  downloadFile(url) синхронно (synchronously), он заморозит все действия пользователя пока не завершится. Таким образом во время скачивания файла пользователь не сможет манипулировать приложением.
Поэтому вы не можете ожидать следующую функцию:
// Call download:

var myfile = downloadFile("http://example.com/file.mp3");

Promise?

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

Promise State?

Один  Promise имеет 3 состояния:
Состояние Описание
Pending Обещание в процессе выполнения, вы не можете знать оно будет успешным или не будет выполнено Например, файл скачивается.
Fulfilled Миссия обещания выполнена. Например, файл успешно скачен.
Rejected Миссия обещания не выполнена. Например, произошла ошибка в процессе скачивания файла.
Синтаксис создания объекта  Promise:
new Promise (
      function (resolve, reject) {
           // Codes
      }
);
Например:
// [ECMAScript 5 Syntax]

var isNetworkOK = true;

// A Promise
var willIGetAFile = new Promise (
    function (resolve, reject) {

        if (isNetworkOK) {
            console.log("Complete the download process!"); // ***
            var file = {
                fileName: 'file.mp3',
                fileContent: '...',
                fileSize: '3 MB'
            };
            resolve(file); // fulfilled
        } else {
            console.log("File download process failed!"); // ***
            var error = new Error('There is a problem with the network.');
            reject(error); // reject
        }

    }
);
Ваша функция  downloadFile(url) возвратит следующий объект  Promise:
// [ECMAScript 5 Syntax]

var isNetworkOK = true;

// This function return a Promise
function downloadFile(url)  {
    console.log("Start downloading file ..."); // ***

    // A Promise
    var willIGetAFile = new Promise (
        function (resolve, reject) {

            if (isNetworkOK) {
                console.log("Complete the download process!"); // ***
                var file = {
                    fileName: 'file.mp3',
                    fileContent: '...',
                    fileSize: '3 MB'
                };
                resolve(file); // fulfilled
            } else {
                console.log("File download process failed!"); // ***
                var error = new Error('There is a problem with the network.');
                reject(error); // reject
            }

        }
    );

    return willIGetAFile; // Return a Promise.
}

2- Используйте Promise

В верхней части у нас есть функция  downloadFile(url), данная функция возвращает объект  Promise, теперь мы изучим как использовать данную функцию.
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");


willIGetAFile
        .then(function (fulfilled) {
            // Get a File
            // Output: {fileName: 'file.mp3', fileContent: '...', fileSize: '3 MB'}
            console.log(fulfilled);
        })
        .catch(function (error) {
             // Network Error!
             // Output: There is a problem with the network.
             console.log(error.message);
        });
OK, это ваш полный код для данного примера:
promise-example.js
// [ECMAScript 5 Syntax]

var isNetworkOK = true;

// This function return a Promise
function downloadFile(url)  {
    console.log("Start downloading file ..."); // ***

    // A Promise
    var willIGetAFile = new Promise (
        function (resolve, reject) {

            if (isNetworkOK) {
                console.log("Complete the download process!"); // ***
                var file = {
                    fileName: 'file.mp3',
                    fileContent: '...',
                    fileSize: '3 MB'
                };
                resolve(file); // fulfilled
            } else {
                console.log("File download process failed!"); // ***
                var error = new Error('There is a problem with the network.');
                reject(error); // reject
            }

        }
    );

    return willIGetAFile; // Return a Promise.
}

// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");


willIGetAFile
        .then(function (fulfilled) {
            // Get a File
            // Output: {fileName: 'file.mp3', fileContent: '...', fileSize: '3 MB'}
            console.log(fulfilled);
        })
        .catch(function (error) {
             // Network Error!
             // Output: There is a problem with the network.
             console.log(error.message);
        });
 
Если  isNetworkOK = true, то при запуске примера вы получите результат:
Если  isNetworkOK = false, то при запуске примера вы получите результат:

3- Chaining Promise

Chaining Promise (Цепь промисов) это набор последовательных  Promise сцепленные вместе.
Представьте вам нужно выполнить 2  действия включая, вызвать функцию  downloadFile(url) чтобы скачать файл из  Internet, потом вызвать функцию  openFile(file) чтобы открыть только что скачанный файл.
Скачивание файла с Internet требует затрату определенного времени, функция  downloadFile(url) смоделирована чтобы возвратить вам обещание ( Promise). Вы сможете иметь файл для открытия только в случае успешного скачивания. Поэтому вам стоить смоделировать функцию openFile(file) возвращающую обещание ( Promise).
Напишите функцию  openFile(file) возвращающую объект  Promise.
function openFile(file) {
    console.log("Start opening file ..."); // ***

    var willFileOpen = new Promise(
        function (resolve, reject) {
             var message = "File " + file.fileName + " opened!"
             resolve(message);
        }
    );

    return willFileOpen; // Return a Promise.
}
Вместо того, чтобы создавать объект  Promise через оператор  new, вы можете использовать статический метод  Promise.resolve(value) или  Promise.reject(error), эти 2 метода возвращают 1 объект  Promise.
Вы можете переписать функцию  openFile(file) выше короче:
// Shorter:

function openFile(file) {
    console.log("Start opening file ..."); // ***
    
    var message = "File " + file.fileName + " opened!"

    // Create a Promise
    var willFileOpen = Promise.resolve(message);
    return willFileOpen;
}

 
Вызовите функцию  downloadFile(url) и  openFile(file) в стиле  Promise:
console.log("Start app.."); // ***

// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");


willIGetAFile
        .then(openFile) // Chain it!
        .then(function (fulfilled) { // If successful fileOpen.
            // Get a message after file opened!
            // Output: File file.mp3 opened!
            console.log(fulfilled);
        })
        .catch(function (error) {
             // Network Error!
             // Output: There is a problem with the network.
             console.log(error.message);
        });


console.log("End app.."); // ***
Смотрите полный код примера:
chaining-promise-example.js
// [ECMAScript 5 Syntax]

var isNetworkOK = true;

// This function return a Promise
function downloadFile(url)  {
    console.log("Start downloading file ..."); // ***

    // A Promise
    var willIGetAFile = new Promise (
        function (resolve, reject) {

            if (isNetworkOK) {
                console.log("Complete the download process!"); // ***
                var file = {
                    fileName: 'file.mp3',
                    fileContent: '...',
                    fileSize: '3 MB'
                };
                resolve(file); // fulfilled
            } else {
                console.log("File download process failed!"); // ***
                var error = new Error('There is a problem with the network.');
                reject(error); // reject
            }

        }
    );

    return willIGetAFile; // Return a Promise.
}

function openFile(file) {
    console.log("Start opening file ..."); // ***

    var willFileOpen = new Promise(
        function (resolve, reject) {
             var message = "File " + file.fileName + " opened!"
             resolve(message);
        }
    );

    return willFileOpen; // Return a Promise.
}

// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");


willIGetAFile
        .then(openFile) // Chain it!
        .then(function (fulfilled) { // If successful fileOpen.
            // Get a message after file opened!
            // Output: File file.mp3 opened!
            console.log(fulfilled);
        })
        .catch(function (error) {
             // Network Error!
             // Output: There is a problem with the network.
             console.log(error.message);
        });
 
Запустите пример и получите результат:

4- Promise является асинхронным

Promise является асинхронным (Asynchronous). Это значит когда вы вызываете функцию  Promise (Функция возвращает 1 Promise) она не замараживает ваше приложение в процессе выполнения обещания.
Чтобы пояснить это, мы немного исправим пример выше. Используя функцию  setTimeout() для симуляции скачивания файла, что займет определенное время. 
chaining-promise-example-2.js
// [ECMAScript 5 Syntax]

var isNetworkOK = true;

// This function return a Promise
function downloadFile(url)  {
    console.log("Start downloading file ..."); // ***

    // A Promise
    var willIGetAFile = new Promise (
        function (resolve, reject) {

            if (isNetworkOK) {
                setTimeout( function() {
                    console.log("Complete the download process!"); // ***
                    var file = {
                        fileName: 'file.mp3',
                        fileContent: '...',
                        fileSize: '3 MB'
                    };
                    resolve(file); // fulfilled
                }, 5 * 1000); // 5 Seconds
            } else {
                var error = new Error('There is a problem with the network.');
                reject(error); // reject
            }

        }
    );

    return willIGetAFile; // Return a Promise.
}


function openFile(file) {
    console.log("Start opening file ..."); // ***

    var willFileOpen = new Promise(
        function (resolve, reject) {
             var message = "File " + file.fileName + " opened!"
             resolve(message);
        }
    );

    return willFileOpen; // Return a Promise.
}

console.log("Start app.."); // ***

// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");


willIGetAFile
        .then(openFile) // Chain it!
        .then(function (fulfilled) { // If successful fileOpen.
            // Get a message after file opened!
            // Output: File file.mp3 opened!
            console.log(fulfilled);
        })
        .catch(function (error) {
             // Network Error!
             // Output: There is a problem with the network.
             console.log(error.message);
        });

console.log("End app.."); // ***
 
 
Результат который вы получите при запуске примера выше:
Обратите внимание на порядок сообщений распечатанные на экране  Console:
Start app..
Start downloading file ...
End app..
Complete the download process!
Start opening file ...
File file.mp3 opened!

5- Promise в ES6

ECMAScript-6 включен в синтаксис стрелочной функции (Narrow Function), поэтому вы можете переписать пример выше по синтаксису  ES6:
chaining-promise-es6-example.js
// [ECMAScript 6 Syntax]

var isNetworkOK = true;

// This function return a Promise
downloadFile = function(url)  {
    console.log("Start downloading file ..."); // ***

    // A Promise
    var willIGetAFile = new Promise (
       (resolve, reject) => {

           if (isNetworkOK) {
               setTimeout( function() {
                   console.log("Complete the download process!"); // ***
                   var file = {
                       fileName: 'file.mp3',
                       fileContent: '...',
                       fileSize: '3 MB'
                   };
                   resolve(file); // fulfilled
               }, 5 * 1000); // 5 Seconds
           } else {
               var error = new Error('There is a problem with the network.');
               reject(error); // reject
           }
        }
    );

    return willIGetAFile; // Return a Promise.
}

openFile = function (file) {
    console.log("Start opening file ..."); // ***

    var willFileOpen = new Promise(
       (resolve, reject) => {
             var message = "File " + file.fileName + " opened!"
             resolve(message);
        }
    );

    return willFileOpen; // Return a Promise.
}


console.log("Start app.."); // ***

// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");


willIGetAFile
        .then(openFile) // Chain it!
        .then(function (fulfilled) { // If successful fileOpen.
            // Get a message after file opened!
            // Output: File file.mp3 opened!
            console.log(fulfilled);
        })
        .catch(function (error) {
             // Network Error!
             // Output: There is a problem with the network.
             console.log(error.message);
        });

console.log("End app.."); // ***

 
Смотрите далее про функцию и стрелу в  ECMAScript:

6- ES7 - Async Await

ECMAScript-7 представляет синтаксис  async и await, они помогают делает использование  Promise более легким и понятным.
chaining-promise-es7-example.js
// [ECMAScript 7 Syntax]

var isNetworkOK = true;

// An Asynchronous function return a Promise
async function downloadFile(url)  {
    console.log("Start downloading file ..."); // ***

    // A Promise
    var willIGetAFile = new Promise (
       (resolve, reject) => {

           if (isNetworkOK) {
               setTimeout( function() {
                   console.log("Complete the download process!"); // ***
                   var file = {
                       fileName: 'file.mp3',
                       fileContent: '...',
                       fileSize: '3 MB'
                   };
                   resolve(file); // fulfilled
               }, 5 * 1000); // 5 Seconds
           } else {
               var error = new Error('There is a problem with the network.');
               reject(error); // reject
           }
        }
    );

    return willIGetAFile; // Return a Promise.
}

// An Asynchronous function return a Promise
async function openFile(file) {
    console.log("Start opening file ..."); // ***

    var willFileOpen = new Promise(
       (resolve, reject) => {
             var message = "File " + file.fileName + " opened!"
             resolve(message);
        }
    );

    return willFileOpen; // Return a Promise.
}

// Main Function (Asynchronous function)
async function mainFunction()  {
    try {
        console.log("Start app.."); // ***

        // Call downloadFile(..) function with 'await' keyword:
        // It returns a File (Not Promise)
        var file = await downloadFile("http://example.com/file.mp3");

        console.log(file);

        // Call openFile(..) function with 'await' keyword:
        // It returns a String (Not Promise)
        var message = await openFile(file);

        console.log(message);

        console.log("End app.."); // ***
    } catch(e)  {
       console.log(e.message);
    }
}

// Call Main Function:
(async () => {
    await mainFunction();
})();