Disadvantages and solutions of JS promise

Posted by jonoc33 on Fri, 24 Sep 2021 14:45:04 +0200

Take an example

I want to print 1 after 1s, 2 after 1s, and 3 after 3s

Traditional asynchronous programming implementation

console.log(new Date)
setTimeout(()=>{
	console.log(new Date,1)
	setTimeout(()=>{
		console.log(new Date,2)
		setTimeout(()=>{
			console.log(new Date,3)
		},3000)
	},2000)
},1000)

 

Promise implementation

console.log(new Date)

let p1 = function(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve()
		},1000)
	})
}

let p2 = function(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve()
		},2000)
	})
}

let p3 = function(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve()
		},3000)
	})
}

p1().then(()=>{
	console.log(new Date,1)
	return p2()
}).then(()=>{
	console.log(new Date,2)
	return p3()
}).then(()=>{
	console.log(new Date,3)
})

 

Another example

I want to print 1 at 1s, 2 at 2s, and splice the results of the first two prints together at 3s

Promise implementation

console.log(new Date)

let p1 = function(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve()
		},1000)
	})
}

let p2 = function(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve()
		},2000)
	})
}

let p3 = function(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve()
		},3000)
	})
}

let v1 = '';
let v2 = '';

p1().then(()=>{
	v1 = 1
	console.log(new Date,v1)
	return p2()
}).then(()=>{
	v2 = 2
	console.log(new Date,v2)
	return p3()
}).then(()=>{
	console.log(new Date,v1+''+v2)
})

Since the return of the then method is used to return the next then caller, the printed result cannot be passed.

Summarize Promise's shortcomings

1. There are too many codes in formula form

Although it is necessary to encapsulate Promise objects and chain call then.

2. The chained call of the then method takes up the return, causing other data to be shared to be locked in the scope of the then method and cannot be provided directly. Only additional global variables can be defined to realize sharing.

Solution: async and await

async and await can be regarded as Promise's syntax sugar, that is, the formula code of big lump is encapsulated to the bottom layer to provide simplified syntax keywords, which are then parsed to the corresponding keywords to automatically generate formula code.

async is used to simplify the definition of Promise encapsulated asynchronous function

async syntax is used to modify a function

async encapsulates the return value of the modified function as a Promise object

That is, the async function can return a Promise object. The status and result of the Promise object depend on the return value of the async function. The rules are the same as those generated by the Promise object returned by the then method.

  That is, if the return value of the async function is a non Promise object, a Promise object with a success status and a result of value is returned.

        If the return value of the async function is a Promise object, it returns an object with a status of value and a result of value.

        If the async function throws an exception, it returns a Promise object with failed status and exception information.

const fs = require('fs')

let p1 = new Promise((resolve,reject)=>{
	fs.readFile('./1.txt','utf8',(err,res)=>{
		if(err) {
			reject(err)
		} else {
			resolve(res)
		}
	})
})

async function fn(){
	let result = null
	let error = null
	fs.readFile('./1.txt','utf8',(err,res)=>{
		error = err//Asynchronous code, post execution
		result = res
	})
	
	if(error){//Synchronization code, execute first
		throw error
	} else {
		return result
	}
}

let p2 = fn()

setTimeout(()=>{
	console.log('p1',p1)
	console.log('p2',p2)
})

The async function return is an example of a non Promise value, which is difficult to give here.

Because we expect the async function to encapsulate asynchronous task actions instead of the original new Promise, but resolve and reject cannot be used inside the async function, we can only rely on return and throw to change the state and result of the Promise object returned by async. The execution of asynchronous tasks must be later than the synchronous codes return and throw.

Therefore, if the return value of the async function depends on the result of the encapsulated asynchronous task, it cannot be implemented.

For an example, see the async function fn in the above code.

Therefore, the return value of async function is usually a Promise object, so it does not depend on return and throw.

in summary:

The async function is applicable to return a Promise object. However, we expect async to solve the formula code problem of new Promise. If we need to define a Promise object as the return value of async function, why go through a layer of async?

In fact, async is not often used alone. Async is usually used with await, because await must be used in async functions.

await is used to simplify the "call of then method"

Await is used to modify an expression syntactically. The result of this expression is generally Promise object or other non Promise object, but it is meaningless to use await modification for expressions with non Promise object.

Because await returns the value of the result of the modified expression (Promise object) (Promise result or Promise value property). If the expression result is a non Promise object, await returns the expression result directly.

Why is await the syntax sugar of the then method?

We can think about how to get the value of a Promise object (that is, the Promise value or Promise result property)

Unfortunately, Promise does not provide the getPromiseValue method to directly provide the value of Promise object. We can only operate the value in the limited scope (callback function) through the then method.

p.then(value=>{

        // Value is the value of Promise object p

})

What problems does this lead to?

Although then chained call solves the "callback hell asynchronous serial programming", chained call also brings another problem, data isolation. That is, data sharing cannot be realized between chains, and each then has its own scope.

for instance

p.then(()=>{

        return database connection

}>. Then ((database connection) = >{

        return database connection. Query table data

}>. Then ((data table) = >{

        Processing table data

        // After data processing, I want to close the database connection, but I find that I can't get the database connection

})

The disadvantage of then chain call is that each then can only obtain the data returned by the previous then, and can not get the data of the previous then by leaps, that is, "my boss's boss is not my boss".

The origin of these problems is that then's return operation is not flexible enough.

await can return the Promise value to solve this problem

async function fn() {

        Database connection = await get database connection

        Table data = await database connection. Query table data

        Processing table data

        Close (database connection)

}

The complete function description of await is: wait for the value of a Promise object and return the value when it arrives. During the waiting process, JS will not skip it to execute the following code, but must wait for it to return before executing the following code.

Topics: Javascript Promise async await