JavaScript:Promise以及Async/Await赢得胜利的原因

2020-10-31大约11分钟

异步函数在JavaScript中是好事。好的方面是异步函数是非阻塞的,因此非常快——尤其是在Node.js上下文中。缺点是处理异步函数可能很麻烦,因为有时您必须等待一个函数完成才能获得其“回调”,然后再执行后面的代码。有几种方法可以发挥异步函数调用的优势并正确处理它们的执行,但是有一种方法远胜于其他方法(你猜对了,就是Async / Await)。在本文中,您将了解Promise的来龙去脉以及Async / Await的用法,以及我们对两者比较的看法。享受吧!

Promise和回调(callback)

作为JavaScript或Node.js开发人员,正确理解Promise和Callback之间的区别以及它们如何协同工作至关重要。两者之间有微小但重要 的差异。在每个Promise的核心,都有一个回调来解决某种数据(或错误),这些数据冒泡到被调用的Promise上。

我们先定义一个函数作为回调函数:

function done(err) {
    if (err) {
        console.log(err);
        return;
    }
    
    console.log('Passwords match!');
}

然后就可以在validatePassword()函数中使用上面的函数:

function validatePassword(password) {
    if (password !== 'my-pwd') {
        return done('Password mismatch!');
    }

    return done(null);
}

如果用Promise呢,我们可以写这样的代码:

// provided a string (password)
function validatePassword(password) {
	// create promise with resolve and reject as params
	return new Promise((resolve, reject) => {
		// validate that password matches my-pwd (the deer)
		if (password !== 'my-pwd') {
			// password doesn't match, return an error with reject
			return reject('Invalid Password!');
		}

		// password matches, return a success state with resolve
		resolve();
	});
}

function done(err) {
	// if an err was passed, console out a message
	if (err) {
		console.log(err);
		return; // stop execution
	}

	// console out a valid state
	console.log('Password is valid!');
}

// dummy password
const password = 'foo';

// using a promise, call the validate password function
validatePassword(password)
	.then(() => {
		// it was successful
		done(null);
	})
	.catch(err => {
		// an error occurred, call the done function and pass the err message
		done(err);
	});

Promise

与传统的基于回调的方法相比,Promise为执行、编写和管理异步操作提供了一种更简单的选择。它们还允许您使用类似于同步try/catch的方法来处理异步错误。Promise还提供了三种独特的状态:

  • 待处理 -Promise的结果尚未确定,因为产生结果的异步操作尚未完成。
  • 已实现 -异步操作已完成,并且Promise具有价值。
  • 已拒绝 -异步操作失败,并且Promise将永远无法实现。在拒绝状态下,Promise会指出操作失败的原因。

当Promise待定时,它可以转换为已实现或已拒绝状态。但是,一旦实现或拒绝了Promise,它就永远不会转变为任何其他状态,其值或失败原因也不会改变。

缺点

Promise没做好的一件事就是解决所谓的“回调地狱”,实际上这只是一系列嵌套函数调用。当然只有一个调用是可以的,很简单。对于许多调用,就容易使您的代码变得难以阅读和维护。

在循环中使用Promise

为了避免使用JavaScript深度嵌套的回调,可以假设您可以简单地遍历Promise,将结果返回到对象或数组,完成后它将停止。不幸的是,这并不容易。由于JavaScript的异步特性,如果您遍历每个Promise,则在代码完成时不会调用Promise的“完成”事件。解决这种情况的正确方法是使用Promise.all()。此功能在标记为完成之前等待所有完成(或第一个拒绝)。