JS-Promise使用

JS-Promise使用

Tans 958 2023-07-31

Promise

最近我一直在学习事件驱动模型和Ajax等组件。在这些组件中,经常会遇到异步操作。虽然在后端开发中,异步操作很常见,但是我作为前端新手,在学习前端时经常使用Ajax进行异步请求,而被其回调处理折磨得死去活来。幸好我遇到了Promise。

Promise 是一种处理异步操作的方式,它可以更好地管理异步结果,并支持链式调用,从而避免了多层嵌套的回调。

Promise对象概念

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

Promise对象代表一个异步操作的最终结果以及其结果值,由于这个未来值还不确定,因此为其定义三个状态:

  • fulfilled 意味着这个操作成功
  • rejected 意味着这个操作失败
  • pending 初始状态, 既不是 fulfilled 也不是 rejected 的状态。

Promise的构造函数

new Promise(executor) 
//上述代码等价于下面代码
new Promise((resolveFunc, rejectFunc)=>{
	resloveFunc(value) //call on resolved, 同时会将状态转换为 fulfilled
	rejectFunc(reason) // call on rejected 同时会将状态转换为 rejected
})

executor: 是一个 function。它接收两个函数作为参数:resolveFuncrejectFunc。在 executor 中抛出的任何错误都会导致 Promise 被拒绝,并且返回值将被忽略。

使用案例

假设我们有三个异步操作,它们有先后顺序,前面的异步请求成功并返回响应之后才能调用下一个异步操作。下面将从传统方法和使用Promise来实现来处理这个逻辑。

Untitled

在传统方法中,我们经常会在异步函数中参数中设置回调函数( Callback Function ),轻而易举写出了以下代码,多个异步操作需要依次执行,并且其中一个异步操作的结果会影响下一个异步操作时,就会导致回调函数嵌套的情况。

// eg.1
doSomethingAsync(function(result1) {
    doSomethingElseAsync(result1, function(result2) {
        doAnotherAsync(result2, function(result3) {
            // 更多嵌套的异步操作
            // ...
        });
    });
});

在上面的例子中,doSomethingAsync 函数返回一个异步结果,并在回调函数中处理该结果。然后, doSomethingElseAsync 函数依赖于第一个异步结果,并在嵌套的回调函数中处理第二个异步结果。这种嵌套的回调结构会导致代码的层次结构变得复杂,很难阅读和维护,从而形成回调地狱。如果以上问题使用Promise来解决,就变成了:

// eg.2
function doSomethingAsync() {
    return new Promise((resolve, reject) => {
        // 异步操作,例如网络请求、定时器等
        // ...

        // 异步操作完成后,调用 resolve 或 reject
        resolve(result);
        // 或
        reject(error);
    });
}

function doSomethingElseAsync(data) {
    return new Promise((resolve, reject) => {
        // 异步操作,例如网络请求、定时器等
        // ...

        // 异步操作完成后,调用 resolve 或 reject
        resolve(result2);
        // 或
        reject(error2);
    });
}

//Promise链式调用
doSomethingAsync()
    .then(result1 => {
        return doSomethingElseAsync(result1);
    })
    .then(result2 => {
        return doAnotherAsync(result2);
    })
    .then(result3 => {
        // 处理最终的结果
    })
    .catch(error => {
        // 处理错误
    });

在上述代码中,我们在 doSomethingAsync 中使用了Promise的链式调用,通过 .then() 来依次处理异步操作的结果,如果其中任何一个操作发生错误,可以使用 .catch() 来进行捕获和处理。后来,JS又加入了 async/await 语法,以便更舒适的使用 promise。

// eg.3
async function handleAsyncOperations() {
    try {
        const result1 = await doSomethingAsync();
        const result2 = await doSomethingElseAsync(result1);
        const result3 = await doAnotherAsync(result2);
        // 处理最终的结果
    } catch (error) {
        // 处理错误
    }
		return result3;
}

handleAsyncOperations();

对于上述代码,有几点需要说明:

  • 被async修饰的函数必须返回一个promise,如果返回的非promise类型,则会自动包装成一个promise类型

    async function foo() {
      return 1;
    }
    //It is similar to:
    function foo() {
      return Promise.resolve(1);
    }
    
  • The await operator is used to wait for a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) and get its fulfillment value. It can only be used inside an async function or at the top level of a module.

  • await/async 可以帮助我们简化代码,无需太多的 then() 语句

关于promise的更多内容,例如其原型方法thenfinallycatch以及常用方法resolve等可以查看MDN官网。

参考资料

  1. MDN-async function
  2. MDN-Promise
  3. Promise练习题