Promise必知必会

前端开辟中常常会举行一些异步操纵,罕见的异步有:

  • 收集要求:ajax
  • IO操纵: readFile
  • 定时器:setTimeout

博客地址

回调

最基础的异步解决方案莫过于回调函数了

前端常常会在胜利时和失利时离别注册回调函数

const req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
    // 胜利的回调
    if (req.status === 200) {
        console.log(req.statusText)
    }
};
req.onerror = function () {
    // 失利的回调
    console.log(req.statusText)
};
req.send();

node的异步api,则一般只注册一个回调函数,经由过程商定的参数来推断究竟是胜利照样失利:

const fs = require("fs");
fs.readFile('input.txt', function (err, data) {
    // 回调函数
    // 第一个参数是err,假如有err,则示意挪用失利
   if (err) {
       return console.error(err);
   }
   console.log("异步读取: " + data.toString());
});

回调的异步解决方案自身也简朴易懂,然则它有一个致命的瑕玷:没法文雅的掌握异步流程

什么意思?

单个异步固然能够很简朴的运用回调函数,然则关于多个异步操纵,就会堕入回调地狱中

// 要求data1胜利后再要求data2,末了要求data3
const ajax = $.ajax({
    url: 'data1.json',
    success: function(data1) {
        console.log(data1);
        $.ajax({
            url: 'data2.json',
            success: function(data2) {
                console.log(data2);
                $.ajax({
                    url: 'data3.json',
                    success: function(data3) {
                        console.log(data3);

                    }
                })
            }
        })
    }
})

这类要按递次举行异步流程掌握的场景,回调函数就显得左支右绌了。这时刻,Promise的异步解决方案就被提了出来。

Promise

当初在学Promise时,看得我真是一脸懵逼,完整不明白这货究竟怎样用。实在,Promise的api要分红两部分来明白:

  1. Promise组织函数:resolve reject (转变内部状况)
  2. Promise对象: then catch (流程掌握)

Promise对象

Promise对象代表一个异步操纵,有三种状况:pending(举行中)、fulfilled(已胜利)和rejected(已失利)

初始时,该对象状况为pending,以后只能变成fulfilled和rejected个中的一个

then要领有两个参数,离别对应状况为fulfilled和rejected时的回调函数,个中第二个参数可选


promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

一般我们会省略then的第二个参数,而改用catch来注册状况变成rejected时的回调函数

promise.then(function(value) {
  // success
}).catch(function(error) {
  // failure
});

Promise组织函数

Promise对象怎样天生的呢?就是经由过程组织函数new出来的。

const promise = new Promise(function(resolve, reject) {
    
});

Promise组织函数吸收一个函数作为参数,这个函数能够吸收两个参数:resolve和reject

resolve, reject是两个函数,由JavaScript引擎供应,不必本身编写

前面我们说过,Promise对象有三种状况,初始时为pending,以后能够变成fulfilled或许rejected,那怎样转变状况呢?答案就是挪用resolve或许reject

挪用resolve时,状况变成fulfilled,示意异步已完成;挪用reject时,状况变成rejected,示意异步失利。

回折衷Promise的对照

实在这里就是Promise最难明白的处所了,我们先看下例子:

回调函数封装

function getURL(URL, success, error) {
    const req = new XMLHttpRequest();
    req.open('GET', URL, true);
    req.onload = function () {
        if (req.status === 200) {
            success(req.responseText);
        } else {
            error(new Error(req.statusText));
        }
    };
    req.onerror = function () {
        error(new Error(req.statusText));
    };
    req.send();
}
const URL = "http://httpbin.org/get";
getURL(URL, function onFulfilled(value) {
    console.log(value);
}, function onRejected(error) {
    console.error(error);
})

Promise封装

function getURL(URL) {
    return new Promise(function (resolve, reject) {
        const req = new XMLHttpRequest();
        req.open('GET', URL, true);
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText);
            } else {
                reject(new Error(req.statusText));
            }
        };
        req.onerror = function () {
            reject(new Error(req.statusText));
        };
        req.send();
    });
}

const URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(function onRejected(error){
    console.error(error);
});

两段代码最大的区分就是:

用回调函数封装的getURL函数,须要显著的传给它胜利和失利的回调函数,success和error的终究挪用是在getURL里被挪用的

用Promise封装的getURL函数,完整不关心胜利和失利的回调函数,它只须要在ajax胜利时挪用resolve(),通知promise对象,你如今的状况变成了fulfilled,在ajax失利时,挪用reject()。而真正的回调函数,是在getURL的表面被挪用的,也就是then和catch中挪用

then要领返回的是一个新的Promise实例(注重,不是本来谁人Promise实例)。因而能够采纳链式写法,即then要领背面再挪用另一个then要领。

function getURL(URL) {
    return new Promise(function (resolve, reject) {
        const req = new XMLHttpRequest();
        req.open('GET', URL, true);
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText);
            } else {
                reject(new Error(req.statusText));
            }
        };
        req.onerror = function () {
            reject(new Error(req.statusText));
        };
        req.send();
    });
}

const URL = "http://httpbin.org/get";
const URL2 = "http://deepred5.com/cors.php?search=ntr";
getURL(URL).then(function onFulfilled(value){
    console.log(value);
    // 返回了一个新的Promise对象
    return getURL(URL2)
}).then(function onFulfilled(value){
    console.log(value);
}).catch(function onRejected(error){
    console.error(error);
});

这段代码就充分说明了Promise关于流程掌握的上风:读取URL的数据后再读取URL2,没有了之前的回调地狱题目。

Promise运用

Promise常常用于对函数的异步流程封装

function getURL(URL) {
    return new Promise(function (resolve, reject) {
        const req = new XMLHttpRequest();
        req.open('GET', URL, true);
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText);
            } else {
                reject(new Error(req.statusText));
            }
        };
        req.onerror = function () {
            reject(new Error(req.statusText));
        };
        req.send();
    });
}
const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
const fs = require('fs')
const path = require('path') 
const readFilePromise = function (fileName) {
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, (err, data) => {
            if (err) {
                reject(err)
            } else {
                resolve(data.toString())
            }
        })
    })
}

连系上面几个例子,我们能够看出Promise封装代码的基础套路:

const methodPromise = function() {
    return new Promise((resolve, reject) => {
        // 异步流程
        if (/* 异步操纵胜利 */){
            resolve(value);
        } else {
            reject(error);
        }
    })
}

Promise.race Promise.all

Promise.all 吸收一个promise对象的数组作为参数,当这个数组里的一切promise对象悉数变成resolve的时刻,它才会去挪用then要领,假如个中有一个变成rejected,就直接挪用catch要领

传给then要领的是一个数组,内里离别对应promise返回的效果

function getURL(URL) {
    return new Promise(function (resolve, reject) {
        const req = new XMLHttpRequest();
        req.open('GET', URL, true);
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText);
            } else {
                reject(new Error(req.statusText));
            }
        };
        req.onerror = function () {
            reject(new Error(req.statusText));
        };
        req.send();
    });
}

Promise.all([getURL('http://deepred5.com/cors.php?search=ntr'), getURL('http://deepred5.com/cors.php?search=rbq')])
.then((dataArr) => {
    const [data1, data2] = dataArr;
}).catch((err) => {
    console.log(err)
})

Promise.race相似,只不过只需有一个Promise变成resolve就挪用then要领

Promise.resolve Promise.reject

Promise.resolve(42); 
// 等价于
new Promise(function(resolve){
    resolve(42);
});

Promise.reject(new Error("出错了"))
// 等价于
new Promise(function(resolve,reject){
    reject(new Error("出错了"));
});
Promise.resolve(42).then(function(value){
    console.log(value);
});

Promise.reject(new Error("出错了")).catch(function(error){
    console.error(error);
});

Promise.resolve要领另一个作用就是将thenable对象转换为promise对象

const promise = Promise.resolve($.ajax('/json/comment.json'));// => promise对象
promise.then(function(value){
   console.log(value);
});

thenable对象指的是具有then要领的对象:

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

非常捕捉

抱负状况下,Promise能够经由过程catch捕捉到非常,然则假如我们没有运用catch,那末虽然掌握台会打印毛病,然则此次毛病并不会停止剧本实行

<script>
const a = b.c.d;
console.log(1); // 代码报错,不会运转到此处
</script>
<script>
console.log(2); // 代码运转
</script>

上述代码只会打印2

<script>
const promise = new Promise((resolve, reject) => {
    const a = b.c.d;
    resolve('ok');
})
promise.then(data => {
    console.log(data)
})
console.log(1); // 代码报错,然则会运转到此处
</script>
<script>
console.log(2); // 代码运转
</script>

打印1和2

解决要领:
window有一个unhandledRejection事宜,特地监听未捕捉的reject毛病

window.onunhandledrejection = function(e) {
    console.log(e.reason);
}
const promise = new Promise((resolve, reject) => {
    const a = b.c.d;
    resolve('ok');
})
promise.then(data => {
    console.log(data)
})

参考

    原文作者:深红
    原文地址: https://segmentfault.com/a/1190000015938310
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞