异步读取文件的几种姿态

臆想的

let fs = require('fs')

function readFile(filename){
    ...
}

let content = readFile('config.js')
// 针对读取到的内容举行操纵,比方打印文件内容
console.log(content)

臆想中,读取文件是有返回值的,将返回值,即文件内容,赋给一个变量,然后决议对读取到的内容举行响应的操纵,比方打印文件中的内容。

简而言之,臆想中,读取文件,打印文件是互相离开的。

回调

let fs = require('fs')

function readFile(filename, callback){
    fs.readFile(filename, 'utf8', (err, data) => {
        if(err){
            throw new err
        }
        callback(data)
    })
}

readFile('config.js', data => {
    console.log(data)
})

实际上,在常常运用的回调中,读取文件和针对文件内容响应的操纵是在一起的,

你在请求读取文件的同时,还要申明猎取文件内容后干吗

这和习惯性头脑,你先把文件内容给我,至于我怎样处置惩罚,稍后再说

Promise

let fs = require('fs')

function readFile(filename) {
    return new Promise(function (resolve) { // 这里的callback,是在run函数中通报的
        fs.readFile(filename, 'utf8', (err, data) => {
            if (err) {
                reject(err)
            }
            resolve(data)
        })
    })
}

let content = readFile('config.js')
content.then(res => {
    console.log(res)
})
// 对照臆想中的
let content = readFile('config.js')
// 针对读取到的内容举行操纵,比方打印文件内容
console.log(content)

运用Promise后,全部誊写逻辑最先和臆想中的很接近了,读取文件和对文件内容的操纵离开了

即经由历程运用Promise,能够将异步的操纵和对异步效果的处置惩罚,离开

来完成一个大略的假的Promise

let fs = require('fs')

function resolve(value) {
    let _self = this
    setTimeout(function () {
        _self.callbacks.forEach(function (callback) {
            callback(value);
        })
    }, 0)
    // 保证在resolve实行之前,then要领都已注册
}

class FakePromise {

    constructor(fn) {
        // fn是个函数,内里包括异步,异步胜利
        // Promise在new的历程中就已最先实行异步代码
        // 异步代码实行完触发resolve,resolve作为异步代码的参数,它早已完成好
        this.value = null
        this.callbacks = []
        fn(resolve.bind(this))
    }

    then(onFulfilled) {
        this.callbacks.push(onFulfilled)
        return this
    }
}

function readFile(filename) {
    return new FakePromise(function (resolve) {
        fs.readFile(filename, 'utf8', (err, data) => {
            if (err) {
                return
            }
            resolve(data)
        })
    })
}

let content = readFile('config.js')
content.then(res => {
    console.log(res)
})

如许看来,Promise和宣布-定阅形式有些相像。promise内部也有个事宜队列,经由历程then注册事宜,经由历程resolve触发。每一个promise在建立的时刻,就最先实行通报给它的函数,函数中会触发resolve,这个resolve就是去实行一切注册的事宜。

固然,实际上promise比这壮大的多,起首resolve实行一切注册的事宜会保证滞后实行,防止还没经由历程then注册完事宜,resolve就实行了

其次,在异步操纵胜利以后,经由历程then注册事宜,能够立马实行,这就需要给promise增加状况机制

...
then(onFulfilled) {
        if (this.state === 'pending') {
            this.callbacks.push(onFulfilled)
            return this
        }
        onFulfilled(value)
        return this
}
...

推断不是’pending’,就马上实行注册的函数

别的就是每一个then()要领,都邑返回一个新的promise

也许长如许

then(onFulfilled) {
    return new Promise(...)
}

参考资料

生成器

let fs = require('fs')

function run(taskDef){
    // 传入的taskDef是个生成器函数,实行后返回迭代器
    let task = taskDef()
    // 挪用迭代器的next()要领,最先taskDef函数中的代码,直至碰到yield,并将yield的值给予result
    let result = task.next()
    function step(){
      if(!result.done){
        if(typeof result.value === 'function'){
          result.value((err, data)=>{
            if(err){
              result = task.throw(err)
              return
            }
            result = task.next(data)
            step()
          })
          // 这里的result.value(...){...}
          // 挪用的是function(callback){fs.readFile(filename, 'utf8', callback)}
        }else{
          result = task.next(data)
          step() // 推断使命是不是实行完
        }
      }
    }
    step()
    // task.next(data) 继承实行生成器中的代码,并将值传回给触发此次next()的yield的等号左侧的变量
}

function readFile(filename){
  return function (callback){ // 这里的callback,是在run函数中通报的
    fs.readFile(filename, 'utf8', callback)
  }
}

run(function*(){
  let content = yield readFile('config.js') // 通报函数至run函数中,并由run通报参数挪用
  // 对文件内容举行处置惩罚
  console.log(content)
})
// 对照臆想中的
let content = readFile('config.js')
// 针对读取到的内容举行操纵,比方打印文件内容
console.log(content)

生成器函数能够住手函数实行,代码在yield readFile('copy.js')处停息

异步使命的回调中挪用迭代器的next()要领,使生成器函数中的代码继承实行,并经由历程next()要领通报参数回至生成器函数中,异步使命完成,返回值已赋值给了content

Promise+generator

let fs = require('fs')

function run(taskDef){
    // 传入的taskDef是个生成器函数,实行后返回迭代器
    let task = taskDef()
    // 挪用迭代器的next()要领,最先taskDef函数中的代码,直至碰到yield,并将yield的值给予result
    let result = task.next()
    function step(){
      if(!result.done){
        let promise = Promise.resolve(result.value)
        promise.then(value => {
            result = task.next(value)
            step()
        }).catch(err => {
            result = task.throw(err)
            step()
        })
          
      }
    }
    step()
    // task.next(data) 继承实行生成器中的代码,并将值传回给触发此次next()的yield的等号左侧的变量
}

function readFile(filename){
  return new Promise(function (resolve, reject){ // 这里的callback,是在run函数中通报的
    fs.readFile(filename, 'utf8', (err, data) => {
        if(err){
            reject(err)
        }
        resolve(data)
    })
  })
}

run(function*(){
  let content = yield readFile('config.js') // 通报函数至run函数中,并由run通报参数挪用
  // 对文件内容举行处置惩罚
  console.log(content)
})

console.log('先实行')

Async&await

是不是是和promise+generator很像

let fs = require('fs')

function readFile(filename) {
    return new Promise(function (resolve) { // 这里的callback,是在run函数中通报的
        fs.readFile(filename, 'utf8', (err, data) => {
            if (err) {
                reject(err)
            }
            resolve(data)
        })
    })
}
(async function test() {
    let content = await readFile('copy.js')
    console.log(content)
})()

参考资料

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