Promise asynchronous programming - the ultimate asynchronous solution async + await node JS primary

Posted by jawapro on Tue, 01 Mar 2022 01:55:50 +0100

Promise introduction:

Promise is a solution for asynchronous programming. ES6 writes it into the language standard, unifies the syntax, and provides promise objects natively.

Promise is simply a container that holds the result of an event (usually an asynchronous operation) that will not end in the future. Syntactically speaking, promise is an object from which you can get the message of asynchronous operation. Promise provides a unified API, and various asynchronous operations can be processed in the same way.

Promise objects have two characteristics:

1. The state of Promise object is not affected by the outside world. The Promise object represents an operation with three states: pending, completed, and rejected. Only the result of asynchronous operation can determine the current state, and no other operation can change this state. This is also the origin of the name Promise, "commitment", which cannot be changed by other means.

2. Once Promise is changed, it will not change again. This result can be obtained at any time. There are only two possibilities for the state of Promise object to change: from pending to fully and from pending to rejected. When these two situations occur, the state will solidify, will not change, and will maintain this result all the time. At this time, it is called resolved. If the state has occurred, you can add a callback function to the Promise object, and you will get this result immediately. This is completely different from events.

There may still be some abstractions, which will be explained in detail next.

Basic use of Promise

The new Promise is a container that can store both asynchronous and synchronous operations

const fs = require("fs");
const path = require("path");

let filePath = path.join(__dirname, "files", "3.txt");


// Asynchronous operations may succeed or fail
// The first formal parameter resolve s the function to be executed when successful
// The second formal parameter reject is the function to be executed when the failure occurs
let p1 = new Promise((resolve, reject)=>{
    //1. Synchronization code
    console.log("Synchronization code");

    // pending, completed, and rejected
    //2. Asynchronous code
    fs.readFile(filePath,"utf-8",(error1, data1)=>{
        if(error1){
            //What you do when you fail
            reject(error1);    // Call P1 The second function of then
        }
        //What to do after reading
        resolve(data1);    // Call P1 The second function of then
    })
});



p1.then((data1)=>{
    console.log("Read successful", data1);
},(error1)=>{
    console.log("read failure", error1);
});

Characteristics of Promise's then chain call

Features of chain call:

1. After the first then is executed, the second then will be executed.

2. The return value of the function in then will be received by the formal parameter of the next then.

3. If a Promse object is returned, the formal parameter of the next then object does not receive the poise object, but the actual parameter when the Promise object calls resolved

const fs = require("fs");
const path = require("path");

let filePath = path.join(__dirname, "files", "3.txt");

// The new Promise is a container that can store both asynchronous and synchronous operations

// Asynchronous operations may succeed or fail
// The first formal parameter resolve s the function to be executed when successful
// The second formal parameter reject is the function to be executed when the failure occurs
let p1 = new Promise((resolve, reject)=>{
    //1. Synchronization code
    // console.log("synchronization code");

    // pending, completed, and rejected
    //2. Asynchronous code
    fs.readFile(filePath,"utf-8",(error1, data1)=>{
        if(error1){
            //What you do when you fail
            reject(error1)
        }
        //What to do after reading
        resolve("resolve Formal parameters of")
    })
});

//The way promise then is written in the industry is called "open train development"

p1.then((data1)=>{
    return p1
},(error1)=>{
    console.log("read failure", error1);
    return error1
}).then((data)=>{
    console.log(data);   // "Resolve" (formal parameter of resolve)
});

Using Promise to read files

//Basic Edition
const fs = require("fs");
const path = require("path");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

let p1 = new Promise((resolve, reject)=>{
    //1. Synchronization code
    // console.log("synchronization code");

    // pending, completed, and rejected
    //2. Asynchronous code
    fs.readFile(filePath1,"utf-8",(error1, data1)=>{
        if(error1){
            //What you do when you fail
            reject(error1);
        }
        //What to do after reading
        resolve(data1)
    })
});
let p2 = new Promise((resolve, reject)=>{
    //1. Synchronization code
    // console.log("synchronization code");

    // pending, completed, and rejected
    //2. Asynchronous code
    fs.readFile(filePath2,"utf-8",(error1, data1)=>{
        if(error1){
            //What you do when you fail
            reject(error1);
        }
        //What to do after reading
        resolve(data1)
    })
});
let p3 = new Promise((resolve, reject)=>{
    //1. Synchronization code
    // console.log("synchronization code");

    // pending, completed, and rejected
    //2. Asynchronous code
    fs.readFile(filePath3,"utf-8",(error1, data1)=>{
        if(error1){
            //What you do when you fail
            reject(error1)
        }
        //What to do after reading
        resolve(data1)
    })
});

let str1 = "";

p1.then((data)=>{
    str1+=data;
    return p2
},(error1)=>{
    console.log("Failed to read file 1", error1);
    return error1
}).then((data)=>{
    str1+=data;
    return p3;
}).then((data)=>{
    str1+=data;
    console.log(str1);
});

You can also encapsulate functions:

const fs = require("fs");
const path = require("path");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

function readFilePromise(filePath){
    return new Promise((resolve, reject)=>{

        fs.readFile(filePath,"utf-8",(error1, data1)=>{
            if(error1){
                //What you do when you fail
                reject(error1);
            }
            //What to do after reading
            resolve(data1)
        })
    });
}

let str1 = "";

readFilePromise(filePath1).then((data)=>{
    str1+=data;
    return readFilePromise(filePath2)
},(error1)=>{
    console.log("Failed to read file 1", error1);
    return error1
}).then((data)=>{
    str1+=data;
    return readFilePromise(filePath3);
}).then((data)=>{
    str1+=data;
    console.log(str1);
});

You can also use util. In the util tool in node, there is a promise method, which is equivalent to encapsulating a function that returns the promise object.

let readFilePromise = util.promisify(fs.readFile);  //This sentence of code is equivalent to the code of the whole function below
// function readFilePromise(filePath){
//     return new Promise((resolve, reject)=>{
//
//         fs.readFile(filePath,"utf-8",(error1, data1)=>{
//             if(error1){
//                 //What you do when you fail
//                 reject(error1);
//             }
//             //What to do after reading
//             resolve(data1)
//         })
//     });
// }

// Summary: util Promise (FS. Readfile) gets a promise object

The complete code for using util is as follows:

const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");
let filePath4 = path.join(__dirname, "files", "data.txt");


let readFilePromise = util.promisify(fs.readFile); 
let writeFilePromise = util.promisify(fs.writeFile);

readFilePromise(filePath1).then((data)=>{
    str1+=data;
    return readFilePromise(filePath2)
},(error1)=>{
    console.log("Failed to read file 1", error1);
    return error1
}).then((data)=>{
    str1+=data;
    return readFilePromise(filePath3);
}).then((data)=>{
    str1+=data;
    console.log(str1);
    writeFilePromise(filePath4, str1);
});

You can also use the all method:

const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");
let filePath4 = path.join(__dirname, "files", "data.txt");

let readFilePromise = util.promisify(fs.readFile);  

let writeFilePromise = util.promisify(fs.writeFile);

Promise.all([readFilePromise(filePath1,"utf-8"), readFilePromise(filePath2,"utf-8"),readFilePromise(filePath3,"utf-8")]).then((data)=>{
    let str1 = data.join("");
    writeFilePromise(filePath4,str1);
}).catch((error)=>{
    //As long as one of P1 and P2 reports an error, the code here will be executed
    console.log(error);
});

Other common methods of Promise

catch() method and finally() method

//Generally, we will put the following codes:
p1.then((data1)=>{
    console.log("Promise success", data1);
},(error1)=>{
    console.log("Commitment failure", error1);
});

//finish writing sth.
p1.then((data1)=>{
    console.log("Promise success", data1);
}).catch((error1)=>{
    console.log("Commitment failure", error1);
}).finally(()=>{
    console.log("Promise that success and failure will execute the code here");
});

race() method

The parameter of race method is an array. If the array element is a Promise instance object, as long as any Promise in the function succeeds, the callback in then will be executed only once.

const fs = require("fs")
const path = require("path")
const util = require('util');

let filePath1 = path.join(__dirname, "files", "1.txt") 
let filePath2 = path.join(__dirname, "files", "2.txt") 

let readFilePromise = util.promisify(fs.readFile);


let p1 = readFilePromise(filePath1,"utf-8")
let p2 = readFilePromise(filePath2,"utf-8")

Promise.race([p1,p2]).then((data)=>{
    // As long as one of P1 and P2 is executed, the code here will be executed once, and the code here will only be executed once
    console.log(123);
    console.log(data);
});

Asynchronous ultimate solution async+await

Basic format:

async function func() {
    let data1 = await promise Object 1;
    let data2 = await promise Object 2;
    let data3 = await promise Object 3;
}
// This is equivalent to having asynchronous function object 1 execute first, then asynchronous function object 2, and then asynchronous function object 3

matters needing attention:

If only one basic data type is written after await, the basic data type will be wrapped into a Promise object.

async function func() {
    let data1 = await 123;
    //1. Only one basic data type is written after await, which will be wrapped into a Promise object
    // That is, data1 is equivalent to: new promise ((resolve, reject) = > {resolve (123)})

    console.log("data1:", data1);   //data1: 123

    return data1
    // return await data1

}

let a = func();

a.then((data)=>{
    console.log(data);  //123 receives the execution result of the Promise object with the above return value
});

If await is followed by Promise, the value of resolve will be returned

await in async function is asynchronous.

async function func() {
    console.log("start-------");
    let data1 = await readFile(filePath1, "utf-8");
    console.log("end-----------");
    let data2 = await readFile(filePath2, "utf-8");
    let data3 = await readFile(filePath3, "utf-8");

    console.log(data1+data2+data3);
}

console.log("start");
func();
console.log("end");

//The output results are as follows:
//start
//start-------
//end
//end-----------

What does this belong to, that is start A macro task, func A macro task, end A macro task, and end-------Caught in an asynchronous event, it enters the callback stack and waits for the end of the macro task

Error handling

1. External processing:

async function func() {
    let data1 = await readFile(filePath1, "utf-8");
    let data2 = await readFile(filePath2, "utf-8");
    let data3 = await readFile(filePath3, "utf-8");

    console.log(data1+data2+data3);

    // writeFile(filePath4, data1+data2+data3)
}

func().catch( error => {
    console.log(error);
} ).finally(()=>{
    console.log("finally");
});



Is to use func External catch Method for error handling

2. Processing inside func:

async function func() {
    try{
        let data1 = await readFile(filePath1, "utf-8");
        let data2 = await readFile(filePath2, "utf-8");
        let data3 = await readFile(filePath3, "utf-8");
    }
    catch (error) {
        console.log(error);
        return
    }
    finally {
        console.log("finally");
    }

    console.log("end");
}

func();

This is the basic use of Promise in node. The main logic is to look at asynchronous macro tasks and micro tasks. Go to my previous articles quickly!!!

Topics: Javascript node.js html5