Promise 基础使用

为什么要使用Promise

JS 是一门单线程单线程的语言,所以在早期解决异步的场景时,大部分情况下是使用回调函数的形式。

例如我们在浏览器中发送一个ajax请求。发送请求之后要过一段时间,浏览器响应之后才会返回给我们结果,如果我们希望在异步请求之后进行某些操作,那么只能通过回调函数来进行。

var syncFunc = function(cb) {
    setTimeout(function() {
        cb();
    }, 1000);
}

syncFunc(function() {console.log(1234)});

$.ajax(url, method, function() {});

像上面的例子,syncName 就是一个异步函数,里面的setTimeout会在1s 之后执行传入的回调函数 cb 。按照上面的方式,在1s之后会打印123。

同样,如果后续还有内容需要在异步函数结束之后操作的话,就需要多个异步函数嵌套,非常不利于维护和阅读。

setTimeout(function() {
    console.log(111);
    setTimeout(function() {
        console.log(222);
        // ...
    }, 1000);
}, 1000);

为了能使回调函数以更加优雅的方式进行调用,在ES6中就产生了一个 Promise 的新的规范,他让异步操作变得几乎像是同步的操作。

Promise 的使用

在支持ES6的现代浏览器环境中,我们可以直接使用 new Promise() 就可以创建一个Promise实例。

这个构造函数接受一个函数,分别接受两个参数。resolve 和 reject, 表示我们需要改变当前实例的转态到 已完成 或者 已拒绝

function promise1() {
    return new Promise(function(resolve, reject) {
        // 定义异步
        setTimeout(function() {
            console.log('promise1');
            // 最后调用传入的 resolve 函数,将该Promise 实例标记为已完成
            resolve();
        }, 1000);
    });
}
function promise2() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('promise2');
            resolve();
        }, 1000);
    });
}

上面两个Promise 实例直接链式调用就可以按顺序执行:

promise1().then(function() {return promise2();});
// 或者简写成
promise1().then(promise2);

上面的代码在浏览器中执行后,就能看到在1s后输出promise1 ,再过1s后输出promise2。从这个demo我们可以看到,当前Promise如果转态变为已完成,即执行了resolve方法治好,就会立即去执行then方法中的下一个Promise实例。

如果我们的Promise实例转态变为了已拒绝,即执行了reject方法,那么就会进入后续的异常处理函数中。

function promise3() {
    return new Promise(function(resolve, reject) {
        var random = Math.random() * 10; 随机生成一个0-10 的随机数
        setTimeout(function() {
            if (random > 5) {
                reject(random);
            } else {
                resolve(random);
            }
        }, 1000);
    });
}

var onResolve = function(val) {
    console.log('resolve input::', val);
}
var onReject = function(val) {
    console.log('reject input::', val);
}

// Promise的.then 方法也可以接收两个函数,第一个函数作为`resolve`后执行,第二个函数作为`reject`后执行
promise3().then(onResolve, onReject);

// 也可以通过 .catch 拦截变为已拒绝的Promise
promise3()
    .catch(onReject)
    .then(onResolve);

// 也可以通过 try catch 进行拦截变为已拒绝的 Promise
try {
    promise3.then(onReject);
} catch(e) {
    onReject(e);
}

这个例子使用了三种方式来拦截Promise变为已拒绝的状态,分别是then的第二个参数,.catch方法捕获抛出的异常,try catch 拦截代码块中的错误。

我们在改变Promise状态是调用的resolev 和reject 函数的时候,也可以向下一步的then或者catch中传递参数。

同时,我们好可以发现,我们早改变Promise转态的时候,还可以给下一步then或者catch中传递参数。

总结一下:

  1. Promise有三种转态,进行中已完成已拒绝,进行中的转态可以更改为已完成或者已拒绝,已经更改过的转态无法继续更改。

  2. ES6中的Promise构造函数,我们构造后需要传入一个函数,这个函数接受两个参数,即resolvereject,执行resolve后,当前Promise变为已完成转态,执行reject之后,当前Promise变为已决绝转态。

  3. 通过.then方法,即可在上一个Promise达到已完成时继续执行下一个函数或者下一个Promise,同时可以通过resolve或者reject传入参数,给下一个函数或者Promise传入初始值。

  4. 已拒绝的Promise,可以通过.catch或者.then的第二个参数或者try catch进行捕获。

封装异步操作为Promise

我们可以将任何接受回调的函数封装成一个Promise,例如封装第一个函数:

// 原函数
function syncFunc(cb) {
    setTimeout(function() {
        cb();
    }, 1000);
}
var callback = function() {
    console.log('success');
}
syncFunc(callback);

// 封装之后
function syncFunc() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve();
        }, 1000);
    });
}
var callback = function() {
    console.log('success');
}

syncFunc().then(callback);

小结:

  1. 我们可以轻松的吧任何一个函数或者异步的函数改为Promise,尤其是异步函数,改为Promise之后可以进行链式调用。
  2. 将带有回调函数的异步改为Promise也很简单,只需要在内部实例化Promise之后,在原理执行回调的地方执行对应更改Promise转态的函数即可。
原文地址:https://www.cnblogs.com/shenjp/p/12482754.html