zoukankan      html  css  js  c++  java
  • ES6--Promise

    Promise的引出

    封装node方法,传入文件路径,可以读取文件并将内容返回

     1 const fs = require('fs')
     2 const path = require('path')
     3 
     4 //封装函数
     5 function getFileByPath(fpath) {
     6     fs.readFile(fpath, 'utf-8', (err, dataStr) => {
     7         if (err) throw err
     8         return dataStr
     9     })
    10 
    11 }
    12 
    13 //readFile为异步处理函数,放到队列里继续运行主程序,直接跳到第10行代码。函数执行发现没有return,默认返回undefined,所以上述函数运行结果都是undefined
    14 
    15 var data = getFileByPath(path.join(__dirname, './files/1.txt'))
    16 console.log(data)  //undefined

    所以上述方法不能拿到文件的数据,进一步改造函数,使用回调:

    function getFileByPath(fpath, callback) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) throw err
            callback(dataStr)
        })
    }
    
    getFileByPath(path.join(__dirname, './files/1.txt'), (data) => {
        console.log(data)
    })

    上面函数,如果传入的文件类型不正确就不能执行后面的代码,可以将文件路径错误时的处理再进一步优化,将错误结果也进行返回

    //将错误结果也进行返回,并规定成功的结果位于回调函数的第二个位置,失败的结果位于回调函数的第一个位置
    function getFileByPath(fpath, callback) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) return callback(err)
            callback(null, dataStr)
        })
    }
    
    getFileByPath(path.join(__dirname, './files/1.txt'), (err, data) => {
        if (err) {
            console.log(err.message)
        } else {
            console.log(data)
        }
    })

    回调函数将成功与失败的结果都在一起处理进行优化,将两种结果分成两个回调函数

    function getFileByPath(fpath, succCb, errCb) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) return errCb(err)
            succCb(dataStr)
        })
    }
    
    getFileByPath(path.join(__dirname, './files/11.txt'), data => {
        console.log('成功的结果为' + data)
    }, err => {
        console.log('失败的结果' + err.message)
    })

    如果有一个需求为,先读取文件1,再读取文件2,最后再读取文件3,调用函数如下

    getFileByPath(path.join(__dirname, './files/1.txt'), data => {
        console.log(data)
    
        getFileByPath(path.join(__dirname, './files/2.txt'), data => {
            console.log(data)
    
            getFileByPath(path.join(__dirname, './files/3.txt'), data => {
                console.log(data)
            })
        })
    })

    这种函数作为参数层层嵌套称为回调地狱

    使用es6中的Promise来解决回调地狱的问题(使用Promise并不会减少代码量,只是将多层的嵌套改成了代码串联的形式)

    Promise概念

    1、promise是一个构造函数,可以通过new创建实例

    2、在Promise上有两个函数,resolve(成功之后的回调函数)和reject(失败之后的回调函数)

    3、在Promise构造函数的prototype属性上,有then方法。通过Promise创造的实例都可以使用then方法

    4、每一个Promise实例表示一个异步操作

    5、通过then方法指定回调函数时,成功的回调函数必须传,失败的回调函数可以不传

    6、Promise实例只要被创建,就会立即执行里面的异步方法

    var promise = new Promise(function () {
        fs.readFile(path.join(__dirname, './files/2.txt'), 'utf-8', (err, dataStr) => {
            if (err) throw err;
            console.log(dataStr)
        })
    })
    //node运行后,会打印出文件内容,说明异步函数被执行
    //也就是说,在使用new关键字后,除了得到Promise实例,还会立即调用我们为Promise构造函数传递的function,执行function中的异步操作代码

    如果不想使函数在调用时才执行,可以将Promise包裹在另一个函数中

    function getFileByPath(fpath) {
        var promise = new Promise(function () {
            fs.readFile(fpath, 'utf-8', (err, dataStr) => {
                if (err) throw err;
                console.log(dataStr)
            })
        })
    }
    
    getFileByPath(path.join(__dirname, './files/2.txt'))

     如果想获得文件读取的结果,直接在readFile中return会像之前函数封装一样,因为异步操作并不能拿到结果,所以需要借助回调函数。Promise为我们提供了成功回调resolve与失败回调reject,在new Promise的函数参数中,传入resolve与reject,代码如下

    function getFileByPath(fpath) {
        var promise = new Promise(function (resolve, reject) {
            fs.readFile(fpath, 'utf-8', (err, dataStr) => {
                if (err) return reject(err);
                resolve(dataStr)
            })
        })
    }
    //resolve与reject为形参

    通过then方法可以指定成功和失败的回调,需要通过Promise的实例调用方法,所以需要将上面函数中的promise返回

    function getFileByPath(fpath) {
        var promise = new Promise(function (resolve, reject) {
            fs.readFile(fpath, 'utf-8', (err, dataStr) => {
                if (err) return reject(err);
                resolve(dataStr)
            })
        })
        return promise
    }

    通过下面方法调用

    var p = getFileByPath(path.join(__dirname, './files/2.txt'))
    
    p.then(data => {
        console.log('读取成功' + data)
    }, err => {
        console.log('读取失败' + err.message)
    })

    使用上面的Promise函数完成“先读取文件1,再读取文件2,最后再读取文件3”需求

    getFileByPath(path.join(__dirname, './files/1.txt')).then(data => {
        console.log('文件1读取成功,开始读取文件2数据')
        getFileByPath(path.join(__dirname, './files/2.txt')).then(data => {
            console.log('文件2读取成功,开始读取文件3数据')
            getFileByPath(path.join(__dirname, './files/3.txt')).then(data => {
                console.log('文件3读取成功')
            })
        })
    })

    可以看到上面的函数依然是层层嵌套,因为这并不是Promise正确的打开方式。优化代码如下:

    Promise的链式调用

    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('aaa')
        }, 1000)
    }).then(res => {
        console.log(`${res},第一层代码`)
        return new Promise(resolve => {
            resolve(res + '111')
        })
    }).then(res => {
        console.log(`${res},第二层代码`)
        return new Promise(resolve => {
            resolve(res + '222')
        })
    }).then(res => {
        console.log(`${res},第三次代码`)
    })

    可这样简写

    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('aaa')
        }, 1000)
    }).then(res => {
        console.log(`${res},第一层代码`)
        return Promise.resolve(res + '111')
    }).then(res => {
        console.log(`${res},第二层代码`)
        return Promise.resolve(res + '222')
    }).then(res => {
        console.log(`${res},第三次代码`)
    })

    更加简洁的方式

    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('aaa')
        }, 1000)
    }).then(res => {
        console.log(`${res},第一层代码`)
        return res + '111'
    }).then(res => {
        console.log(`${res},第二层代码`)
        return res + '222'
    }).then(res => {
        console.log(`${res},第三次代码`)
    })

    Promise捕获异常

    上面代码中并没有对异常进行处理。当其中一个路径不正确时,例如将1.txt替换成11.txt,其中11.txt是不存在的

    getFileByPath(path.join(__dirname, './files/11.txt')).then(data => {
        console.log('文件1读取成功,开始读取文件2数据' + data)
    
        return getFileByPath(path.join(__dirname, './files/2.txt'))
    }).then(data => {
        console.log('文件2读取成功,开始读取文件3数据' + data)
    
        return getFileByPath(path.join(__dirname, './files/3.txt'))
    }).then(data => {
        console.log('文件3读取成功' + data)
    })
    
    console.log('OK')

    上面代码执行后,会先打印出'OK'再报错。因为报错所在的函数是异步操作。

    1、如果前面的Promise执行失败后不影响后面的Promise执行,这样做:

    getFileByPath(path.join(__dirname, './files/11.txt')).then(data => {
        console.log('文件1读取成功,开始读取文件2数据' + data)
    
        return getFileByPath(path.join(__dirname, './files/2.txt'))
    }, err => {
        console.log('这是失败的信息' + err.message)
        //需要return一个新的Promise不影响后面的.then
        return getFileByPath(path.join(__dirname, './files/2.txt'))
    }).then(data => {
        console.log('文件2读取成功,开始读取文件3数据' + data)
    
        return getFileByPath(path.join(__dirname, './files/3.txt'))
    }).then(data => {
        console.log('文件3读取成功' + data)
    })

    2、如果后续的Promise执行依赖于前面的Promise结果,前面的Promise失败后,则立即终止所有的Promise执行,通过.catch

    getFileByPath(path.join(__dirname, './files/11.txt')).then(data => {
        console.log('文件1读取成功,开始读取文件2数据' + data)
    
        return getFileByPath(path.join(__dirname, './files/2.txt'))
    }).then(data => {
        console.log('文件2读取成功,开始读取文件3数据' + data)
    
        return getFileByPath(path.join(__dirname, './files/3.txt'))
    }).then(data => {
        console.log('文件3读取成功' + data)
    }).catch(err => {   //如果前面有任何的Promise执行失败,会立即终止Promise执行,并进入catch中
        console.log('这是错误处理'+err.message)
    })

    Promise的三种状态

    • pending :等待状态,比如正在进行网络请求或者定时器没有到时间
    • fulfill : 满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
    • reject : 拒绝状态,当我们主动回调了reject时就处于该状态,并且会回调.catch()

    Promise.all

    需求:在两个请求都结束后进行xxx操作

    普通回调

    let isResult1 = false
    let isResult2 = false
    $.ajax({
        url: 'url1',
        success(data) {
            isResult1 = true
            handleResult()
        }
    })
    $.ajax({
        url: 'url2',
        success(data) {
            isResult2 = true
            handleResult()
        }
    })
    
    function handleResult() {
        if (isResult1 && isResult2) {
            //执行代码
        }
    }

    使用all

    Promise.all([
        new Promise((resolve, reject) => {
            $.ajax({
                url: 'url1',
                success(data) {
                    resolve(data)
                }
            })
        }),
        new Promise((resolve, reject) => {
            $.ajax({
                url: 'url2',
                success(data) {
                    resolve(data)
                }
            })
        })
    ]).then(results => {
        results[0]  //url1的结果
        results[1]  //url2的结果
    })
  • 相关阅读:
    How to interpret complex C/C++ declarations (ZT)
    The Managed Thread Pool
    How slow is dynamic_cast?
    Type Safety
    sBRDF空间双向反射分布函数完全解析
    近日工作与生活梗概
    简单的环绕散射 Simple Wrap Diffuse From GPU GEMS1
    屈辱史
    难以忽视的细节
    物理学与计算机图形学中的HDR
  • 原文地址:https://www.cnblogs.com/lianglanlan/p/9857288.html
Copyright © 2011-2022 走看看