Non-blocking I/O for Node.js

Definition: I/O is Input/Output, the Input and Output of a system, an exchange of external system information. Speaking is output, listening is input. The difference between blocking I/O and non-blocking I/O is whether the system can accept other input during the period from receiving input to receiving output.

Example: Eating

  • Go to the canteen to have a meal: wait in line for a meal, wait for the person in front of you to finish the meal, it is your turn to eat, wait for you to finish the meal and then start eating (block I/O)
  • Go to a restaurant to order food: Sit down, order food, wait for the waiter to serve you and then go to someone else, while you go about your business, wait for the kitchen to make your food and serve it to you (non-blocking I/O)

Canteen aunt and waiter = system, input = order, output = serve the dish canteen aunt can only play a dish, you order to the canteen aunt (input), the aunt to you (output), this is blocked I/O, you can not order during the cooking; This is non-blocking I/O and allows the server to take someone else’s order (input) until your order is ready for you (output)

To understand non-blocking I/O, you need to determine the system boundaries. In the example above, you can’t understand non-blocking I/O if you think of the cook as a system;

Node.js normally does all of its I /o operations non-blocking, and distributes a lot of computing power to other C++ threads. When the other C++ threads are finished, they call back the results to the nodejs thread, which then returns the results to your application

// Non-blocking IO
// The second argument is passed to a callback function that evaluates to 1+1
var result = null;
glob(__dirname + '/ / * * *'.function (err, res) {
  result = res;
  console.log(result);
});
console.log(1 + 1);
Copy the code

Callback Callback function

Nodejs has a large amount of non-blocking I/O, and the results of these non-blocking I/O programs need to be retrieved through callback functions. The way to program through callback functions is asynchronous programming

The callback function follows a rule that the first argument is error and the subsequent argument is the result

function interview(callback) {
  setTimeout(() = > {
    if (Math.random() < 0.8) {
      callback('success');
    } else {
      throw new Error('fail'); }},500);
}
try {
  interview(function (val) {
    console.log(val);
  });
} catch (e) {
  console.log('error', e);
}
Copy the code

The above code calls the functionthrow errorDon’t betry catchInstead, it is thrown directly to nodeJS globallyWhy is that? Becausetry catchandThe call stackIs very relevant, the code will exist function call function situation, each function call other functions will add a layer in the call stack, layer by layer to form a chain system of calls, in the program to form a stack structuretry catchSo if you put a try catch on the fifth item on the stack, the top element throws an error, and that error goes up the stack, and it gets caught on the fifth item; The setTimeout function in the above code is anotherEvent loopInside the callback, the new event loop is a whole new call stack, sothrow new ErrorIn a setTimeout asynchronous task, a throw new Error will not be caught by a try catch, so the Error will need to be passed through the callback.

function interview(callback) {
  setTimeout(() = > {
    if (Math.random() < 0.8) {
      callback('success');
    } else {
      callback(new Error('fail')); }},500);
}
interview(function (res) {
  if (res instanceof Error) {
    return console.log('car');
  }
  console.log(res);
});
Copy the code

However, the nodeJS callback has many arguments, so the convention is that the first argument is error. The second and third are the result of the callback

Event loop

Event loop example In a restaurant, for example, a customer ordered a eggplant, waiter told the kitchen do a eggplant, create a thread of eggplant, then another guest ordered a sprinkle of meat, the waiter told the kitchen make a starch meat, and to a guest order a tomato scrambled eggs, the waiter told the kitchen do a tomato scrambled eggs; Suppose that the fish-fragrant eggplant is done, the service will bring the fish-fragrant eggplant to you in front of the end of the thread, at this time the tomato fried eggs, the waiter also put the tomato fried eggs out, and finally the small fried meat, the restaurant’s task is completed; So this is an example of an event loop, which is a loop that handles events, and as soon as one of the events is done it calls back the data needed for that event;

const eventLoop = {
  queue: []./ / an empty queue
  loop() {
    while (this.queue.length) {
      var callback = this.queue.shift();
      callback();// at the bottom of the stack, the code up to this callback is all c++
    }
    setTimeout(this.loop.bind(this), 50);
  },
  add(callback) {
    this.queue.push(callback); }}; eventLoop.loop();setTimeout(() = > {
  eventLoop.add(function () {
    console.log(1);
  });
}, 500);

setTimeout(() = > {
  eventLoop.add(function () {
    console.log(2);
  });
}, 800);
Copy the code

Each event loop is a new call stack, and the point of the call stack is an event loop trigger, which can be understood as the first callback().

Promise

The result that the current event loop does not get, the result that the future event loop will get, is a state machine

  • pending
  • Very sad /resolved
  • Rejected (对)
var promise = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    // resolve(2);
    reject(new Error());
  }, 200);
})
  .then((res) = > {
    // console.log(res);
  })
  .catch((error) = > {
    console.log(error);
  });

console.log(promise);
Copy the code

. And then. The catch

  • The Promise in the Resolved state will call back the first. Then
  • A Promise in the Rejected state calls back to the first. Catch
  • Any Promise in the Rejected state that is not followed by a. Catch will cause a global error in the browser/Node environment

Promise solves many asynchronous process control problems, and the code for the callback above is rewritten as Promise

var promise = interview();
function interview() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (Math.random() < 0.8) {
        resolve('success');
      } else {
        reject(new Error()); }},500);
  });
}
promise
  .then((res) = > {
    console.log(res);
  })
  .catch((error) = > {
    console.log(error);
  });
Copy the code

Executing then and catch returns a new Promise whose final state is determined by the result of executing the callbacks to then and catch

  • If the callback function ends up as a throw, the promise is in the Rejected state
var promise = interview();
var promise2 = promise.then((res) = > {
  throw new Error(a); });setTimeout(() = > {
  console.log(promise);
  console.log(promise2);
}, 800);
function interview() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (Math.random() < 0.8) {
        resolve('success');
      } else {
        reject(new Error()); }},500);
  });
}
Copy the code

  • If the callback ends up being a return, the promise is resolved, and the return is resolved even in a catch

  • If the callback function eventually returns a Promise, the Promise is kept in line with the Promise of the callback function return, so that the task can be executed sequentially in the chained invocation of the Promise
var promise = interview();
var promise2 = promise.then((res) = > {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('I accept');
    }, 400);
  });
});

// Print at 800ms and 1000ms
setTimeout(() = > {
  console.log(promise);
  console.log(promise2);
}, 800);
setTimeout(() = > {
  console.log(promise);
  console.log(promise2);
}, 1000);


function interview() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (Math.random() < 1) {
        resolve('success');
      } else {
        reject(new Error()); }},500);
  });
}
Copy the code

At 800ms, the promisE2 state is still pending, and the printed promisE2 state is the same as the reUTn New promise state.

So the chain call can be rewritten as

var promise = interview(1)
  .then((res) = > {
    // Then return a promise, subsequent operations will wait for the promise to complete
    return interview(2);
  })
  .then((res) = > {
    return interview(3);
  })
  .then((res) = > {
    console.log('I laughed');
  })
  .catch((error) = > {
    console.log('I cried' + error.round);
  });

function interview(round) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (Math.random() > 0.3) {
        resolve('success');
      } else {
        var error = new Error('fail'); error.round = round; reject(error); }},500);
  });
}
Copy the code

Async promise.all ([]) promise.all ([]) receives an array and is called when all promises in this array are resolved

Promise.all([interview('geekbang'), interview('tencent')])
  .then((res) = > {
    console.log('I laughed');
  })
  .catch((error) = > {
    console.log('I' + error.name + 'hung up');
  });
function interview(name) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (Math.random() > 0.3) {
        resolve('success');
      } else {
        var error = new Error('fail'); error.name = name; reject(error); }},500);
  });
}
Copy the code